首页 > 代码库 > [DP] 背包问题大总结
[DP] 背包问题大总结
@kaike
1.01背包
有N件物品和一个容量为C的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使价值总和最大。
每种物品仅有一件,可以选择放还是不放。
用f[i][c]表示前i件物品恰好放入容量为c的背包里面,总价值最大。
f[i][c]=max{f[i-1][c],f[i-1][c-w[i]]+v[i]}
若不放第i件,就是前i-1件物品放入容量为c的背包里 f[i-1][c]
若放第i件,把前i-1件物品放入容量为 c-w[i] 的背包里,f[i-1][c-w[i]],总价值加上v[i]。
f[i][c]是由 f[i-1][c] 和 f[i-1][c-w[i]] 推出来的
如何在推f[i][c]的时候已知f[i-1][c] 和 f[i-1][c-w[i]]呢
f[i-1][c]就是不要这件物品,直接去取一个值
f[i-1][c-w[i]]就是要这件物品,不过你要取空出这个物品重量的最大价值量的值
由于刚开始时不能出现负值,第一次的时候c不能直接从0开始,需要一个缓冲。
于是二循环就要从背包容量开始倒推
注意看
f[i][c]的值只与f[i-1][c]和f[i-1][c-w[i]]有关,
如此倒推,完全可以转化为 f[c]=max{f[c],f[c-w[i]]+v[i]}
外面的f[c]是新值,里面的f[c]是上次循坏的值也就是f[i-1][c]
f[c-w[i]]同理也是上次循环的值
这样数组从二维到了一维,可以省空间。
时间复杂度(n*c)空间复杂度(c)
最大值当然要从背包满的时候f[c]处取得辣
1 for(int i=1;i<=n;i++)2 for(int j=c;j>0;j--)3 f[j]=max(f[j],f[j-w[i]]+v[i]);
细节问题
1.要求恰好装满背包,f[0]=0,f[1-v]=-∞
2.没要求恰好装满,f[0-v]=0
若要求恰好装满,则只要容量为0的背包恰好装满,而其他的都是未定义的状态
若不要求,所有值都有个确定值
2.完全背包问题
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
与01背包不同的是每件可以取无限件
问题十分棘手啊
f[i][c]=max{f[i-1][v-k*w[i]]+k*v[i]} 0<=k*w[i]<=v
若两件物品i、j满足c[i]<=c[j]且w[i]>=w[j],则将物品j去掉,不用考虑。
看起来小优化并没有优化掉什么复杂的东西,毕竟遇到一些极值还是要奔溃的
可以从01背包问题获得点启示
01背包二循环是从c-0开始的,因为他要先预留出来f[c-w[i]]的值
而完全背包就完全不用考虑这个问题,
我们从0-c开始循环
在考虑是否加这个物品的时候,可能正需要一个正好已选入第i个物品的值,所以就正着循环
1 for(int i=1;i<=n;i++)2 for(int j=0;j<=c;j++)3 f[j]=max(f[j],f[j-w[i]]+v[i]);
[DP] 背包问题大总结