多重背包
【题目描述】
现有一个容量为M的背包和N种物品,每种物品都有三个属性Vi、Wi、Ci,分别表示这种物品的体积、价值以及件数。
要求从所给的物品中选出若干件,其体积之和不能超过背包容量,且使所选物品的价值之和最大。
【输入描述】
第一行输入两个整数N、M;
接下来N行,每行输入三个整数Vi、Wi、Ci。
【输出描述】
输出一个整数,表示最大的价值之和。
【样例输入】
2 8
2 100 4
4 100 2
【样例输出】
400
【数据范围及提示】
对于20%的数据,Ci=1;
对于60%的数据,N,M <= 500,Ci <= 100;
对于100%的数据,N,M <= 3000,Ci <= 3000。
源代码: #include<cstdio> #include<cstring> int N,M,f[6001]; //最好开两倍,不明觉厉的数据范围。 void Solve(int V,int W,int C) { if (V>M) //特判直接退出。 return; int Head,Tail,Q[6001],F[6001]; for (int a=0;a<V;a++) //实在装不下该物品的冗余空间。 { Head=1; Tail=0; Q[++Tail]=a; F[a]=f[a]; //初始化。 for (int b=V+a;b<=M;b+=V) //枚举装下几个该物品的空间。 { while (Head<=Tail&&f[Q[Tail]]-Q[Tail]/V*W<=f[b]-b/V*W) //若此时的冗余空间价值大于队尾的。 Tail--; Q[++Tail]=b; //Q[]存储的是空间。 if ((b-Q[Head])/V>C) //若数量超过则去头,因为顺序循环,所以不必使用while()。 Head++; F[b]=f[Q[Head]]-Q[Head]/V*W+b/V*W; //取最大冗余价值,然后加上应得的物品价值。 } memcpy(f,F,sizeof(F)); //小白鼠数组。 } } int main() //多重背包+单调队列优化。 { scanf("%d%d",&N,&M); for (int a=1;a<=N;a++) { int V,W,C; scanf("%d%d%d",&V,&W,&C); Solve(V,W,C); } printf("%d",f[M]); return 0; } /* 想一想,为什么这样处理是正确的呢? 首先会发现,在处理过程中,所有空间情况都只会处理一遍。 其次,冗余空间的价值不仅包括非物品价值,还包括了比物品更多的价值。 其实上面都是废话。 多重背包问题:f[i][j]=max(f[i-1][j-k*V[i]]+k*W[i]) (0 <= k <= C[i]); 把它变一变,设A=j/V[i]、B=j%V[i],并用k替换(A-k),于是状态转移方程变为: f[i][j]=max(f[i-1][B+k*V[i]]-k*W[i])+A*W[i] (A-C[i] <= k <= A)。 */