单调队列优化多重背包
单调队列优化
多重背包
我们尝试将
朴素的代码:
for (int i = 1; i <= n; i++){ //阶段,枚举到第几类物品
for (int u = 0; u < v[i]; u++){ //枚举余数
for (int p = (m-u)/v[i]; p >= 0; p--){
//因为枚举的当前容量 j = u+p*v[i] 满足 0 <= u+p*v[i] <= m
//所以 p*v[i] <= m-u。p <= (m-u)/v[i]
//由于不能重复转移,采用倒序枚举
for (int k = p-c[i]; k <= p-1; k++){
//j 由 k*v[i]+u 转移来,c为每类物品的的物品个数
int j = u+v[i]*p;
int j_ = u+v[i]*k;
f[j] = max(f[j],f[_j]+(p-k)*w[i]); // w[i] 为物品价值
}
}
}
}
从上面的程序上可以容易看出,在
改成单调队列优化思路和朴素
int calc(int i,int u, int k){
return f[u+k*v[i]]-k*w[i];
}
int main(){
for (int i = 1; i <= n; i++){ //阶段,枚举到第几类物品
for (int u = 0; u < v[i]; u++){ //枚举余数
int l = 1, r = 0;
int maxp = (m-u)/v[i];
for (int k = maxp-1; k >= max(maxp-c[i],0); k--){ // 初始的决策集合
while(l <= r && calc(i,u,q[r]) <= calc(i,u,k)) r--;
q[++r] = k;
}
for (int p = maxp; p >= 0; p--){
while(l <= r && q[l] > p-1) l++; //排除过时决策
f[u+p*v[i]] = max(f[u+p*v[i]], calc(i,u,q[l])+p*w[i]);//拿队头更新答案(把p*w[i]分离出来了)
if (p-c[i]-1 >= 0){//边界条件,是否加入新决策。注意这里含零。
while(l <= r && calc(i,u,q[r] <= calc(i,u,p-c[i]-1))//踢出队尾比值它小年龄还比它大的
q[++r] = p-c[i]-1;
}
}
}
}
}