赞
踩
1.传统的二维数组,第i件物品的重量为w[i],价值为v[i]
dp[i][j]保存的是选择前i件物品(每一件物品的状态为选与不选),在背包容量为j的情况下,可以获得的最大价值dp[i][j]=max(dp[i-1][j],dp[i-w[i]][j]).dp[i-1][j]是第i件背包不选,dp[i-1][j-w[i]]是第i件背包选,注意dp[i-1][j-w[i]]在以前就已经算出来了,可以直接用,而且里面保存的一定是最优值。
关键代码:
for(int i=1;i<=N;i++)
for(int j=0;j<=V;j++)//千万要记得重量要从0开始,不是1
{
if(w[i]<=j)
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+value[i]);
else
dp[i][j]=dp[i-1][j];
}
总结:dp[i][j]是当前选择前i件物品(每一件物品的状态为选与不选),在背包容量为j的情况下所获得最大价值,也就是局部最优解,在进行循环的过程中,需要用到前面已经计算过的值,这就体现到了动态规划的思想。
2.优化后的使用一维数组,第i件物品的重量为w[i],价值为v[i]
dp[j]保存的是当前容量背包为j的情况下,所获得最大价值。
先来看关键代码:
for(int i=1;i<=N;i++)
for(int j=V;j>=w[i];j--)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
外层循环是第i件物品,当内层循环j循环一遍后,dp[j]保存的也是选择前i件所获得的最大值。但是它并不是单纯的依赖于某一件物品了(因为一维数组,没有二维数组中的[i],它是在前面所有状态中,dp[j]是最优的那一个。注意到内层循环j的值是从大到小的,为什么? 从小到大会怎么样,假设在选择第i件物品时,j的值从小到大,那么算出的dp[w[i]] , dp[w[i]+1] ,dp[w[i]+2]。。。里面的下标也是从小到大的,但是注意状态转移方程dp[j]=max(dp[j],dp[j-w[i]]+v[i]);dp[j-w[i]]下标肯定比d[j]要小,也就是说在内层循环中,dp[j-w[i]]是肯定要被先算出来的,我们假设它的值是在第i件物品的状态为被选择时得出的,那么在算到dp[j]时,有一种情况,是 dp[j] <dp[j-w[i]]+v[i],这意思是,第i件物品还得选,价值要求最大,但前面说的dp[j-w[i]]第i件物品已经被选择过了,再选的话这就不是01背包了(01背包要求每件物品只能选择一件),所以内层循环j的值不能从小到大,从大到小,保证了在外层循环固定为i的情况下,内层循环计算过程中,dp[j-w[i]]是后于dp[j]算出来的,这里dp[j-w[i]]是外层循环1到i-1时得出来的值,也就是前i-1件物品所得出的最优值。这样也就保证了,在内层循环中,第i件物品只能被选0次或1次,不能多选。
完全背包(每种物品可以选择无穷多件):
通过前面01背包的第二种情况的解释,我们自然而然得可以写出其关键代码:
for(int i=1;i<=N;i++)
for(int j=w[i];j<=V;j++)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。