您的位置:首页 > 其它

[动态规划] 01背包与完全背包

2014-11-08 09:40 274 查看
01背包(每种物品的状态为选择或不选择,最多只能选1件):
1.传统的二维数组,第i件物品的重量为w[i],价值为v[i]

dp[i][j]保存的是选择前i件物品(每一件物品的状态为选与不选),在背包容量为j的情况下,可以获得的最大价值

两种情况:

一.当前背包容量j<第i件的重量时,第i件背包肯定不能选,放不下去,有dp[i][j]=dp[i-1][j]

二.当前背包容量j>=第i件的重量时,第i件背包可以选,能放得下去,但因为要考虑到所获得最大价值,所以这时候有两种选择,选或不选,我们要在其中取最大值。状态转移方程为:
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]);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: