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

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]);

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值