背包问题是动态规划的常见题目。主要分为01背包、多重背包等。题目一般给出物品个数N、背包体积V。然后输入每个物品的体积V和价值W
一般的解题思路是使用一个二维数组,每一个f[i][j]可以看作一个背包。那么f[i][j]就表示有i个物品放入体积为j的背包最大的价值。对于第i个物品可能出现三种情况:
- 放不下第i个物品 f[i-1][j]
- 可以放下第i个物品,但是不放 f[i-1][j]
- 可以放下第i个物品,放 f[i-1][j-v[i]]+w[i](其中v[i]表示第i个物品的体积,w[i]表示第i个物品的价值
那么
if(j>=volumn[i]){//可以放下 f[i][j]=max(f[i-1][j],f[i-1][j-volumn[i]]+weights[i]); } f[i][j]=max(f[i][j],f[i-1][j]);//如果放不下或者可以放下和放不下进行比较
二维数组的空间复杂度为O(n2),我们可以使用滚动数组的方式来优化空间复杂度。可以看到第i个状态只和i-1的状态有关。那么我们有两种方式进行优化。
1.交替滚动
也就是使用前一个时刻和当前时刻相互进行更新,使用f[2][N]的二维数组。
int pre=1; int now=0; for(int i=1;i<=N;i++){ swap(now,pre); for(int j=1;j<=V;j++{ if(v[i]<=j) f[now][j]=max(f[pre][j],f[pre][j-v[i]]+w[i]); f[now][j]=max(f[now][j],f[pre][j]); } }
此时我们的j从小到大和从大到小都可以。
2.自我滚动
使用一维数组进行自我更新
for(int i=1;i<=n;i++) for(int j=V;j>=V[i];j--)//这里必须从大到小 { f[j]=max(f[j],f[j-V[i]]+W[i]); }
为什么需要从大到小?
如果我们从小到大,那么当我们更新j较大的值的时候。此时一维数组f[i]前面存放的不是i-1的状态而是i的状态。由于是01背包,每个物品只有一个。因此我们不能重复放入一个相同的物品。因此我们需要从大到小进行更新,因为从大到小的时候,假如计算到j在中间大小的时候,我们更新用到的体积较小的i,f[j]=max(f[j],f[j-V[i]]+w[i])此时f[j-V[i]]这个地方存放的是i-1时刻的状态,因为从大到小更新的时候f[小]肯定是在f[大]之后进行更新的,所以保存的是上一个时刻的状态即换成二维数组是f[i-1][j]。
如果我们的物品可以无限次放入,那么就是完全背包问题。此时我们需要从小到大更新,因为一个小物品可能被多次放入,我们更新大物品的时候需要的是此时刻的状态。
滚动数组的缺点:
无法记录状态转移的信息,覆盖了中间的状态。导致无法输出具体的方案。
标签:状态,背包,更新,问题,01,从大到,数组,物品 From: https://www.cnblogs.com/hailanben/p/17256806.html