您的位置:首页 > 其它

单调队列优化多重背包

2016-10-19 10:44 316 查看
多重背包的最原始的状态转移方程:

令 c[i] = min(num[i], j / v[i])

f[i][j] = max(f[i-1][j-k*v[i]] + k*w[i]) (1 <= k <= c[i]) 这里的 k 是指取第 i 种物品 k 件。

如果令 a = j / v[i] , b = j % v[i] 那么 j = a * v[i] + b.

这里用 k 表示的意义改变, k 表示取第 i 种物品的件数比 a 少几件。

那么 f[i][j] = max(f[i-1][b+k*v[i]] - k*w[i]) + a*w[i] (a-c[i] <= k <= a)

可以发现,f[i-1][b+k*v[i]] - k*w[i] 只与 k 有关,而这个 k 是一段连续的。我们要做的就是求出 f[i-1][b+k*v[i]] - k*w[i] 在 k 取可行区间内时的最大值。

k 取可行区间内时的最大值。k 取可行区间内时的最大值。k 取可行区间内时的最大值。

这句话特别重要,两个晚上理解了这句话。

m[i] = min(n[i], j / v[i])。

F[i][j]就是求这个队列最大长度为m[i] + 1时,队列中元素的最大值,加上a * w[i]。

这就可以使用单调队列优化。

for (int i = 1; i <= n; ++i) {
Ni = Num[i]; Vi = V[i]; Wi = W[i];
for (int j = 0; j < Vi; ++j) {
Head1 = Tail1 = 0;
Head2 = Tail2 = 0;
Cnt = 0;
for (int k = j; k <= m; k += Vi) {
if (Tail1 - Head1 == Ni + 1) {
if (Q2[Head2 + 1] == Q1[Head1 + 1]) ++Head2;
++Head1;
}
t = f[k] - Cnt * Wi;
Q1[++Tail1] = t;
while (Head2 < Tail2 && Q2[Tail2] < t)
--Tail2;

Q2[++Tail2] = t;

f[k] = Q2[Head2 + 1] + Cnt * Wi;//可行性区间为cnt
++Cnt;
}
}
}


f[i][j] = max(f[i-1][b+k*v[i]] - k*w[i]) + a*w[i] (a-c[i] <= k <= a)

例题

http://www.cnblogs.com/JoeFan/p/4168024.html

codevs

5429

#include<iostream>
using namespace std;
int f[10000],num[10000],v[10000],w[10000];int ps,qs,pe,qe;
int q1[10000],q2[10000];
int main()
{  int n,m;cin>>n>>m;
for (int i=1;i<=n;++i)
{  cin>>w[i]>>v[i]>>num[i];
}
int ans=0;
for (int i=1;i<=n;++i)
{  for (int j=0;j<w[i];++j)
{  ps=qs=1;//q为单调,q2单调
qe=pe=0;
for (int k=j,cnt=0;k<=m;++cnt,k+=w[i])
{  if (pe-ps==num[i])
{  if (q1[ps]==q2[qs])
++qs;
++ps;
}
int t=f[k]-cnt*v[i];
q1[++pe]=t;
while (qs<=qe&&t>q2[qs])
--qe;
q2[++qe]=t;
f[k]=q2[qs]+cnt*v[i];
}
}
ans=max(ans,f[m]);
}
cout<<ans;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: