完全背包--动态规划

题目:
有N种物品和一个容量为V的背包,每种物品都有无限件可用。放入第i种物品的费用是Ci,价值是Wi,求解:将哪些物品装入背包,可使这些物品耗费的费用和不超过背包容量,且价值总和最大。
分析:
(一)建立状态方程
可以转化为01背包问题求解
dp[i][v]表示前i件种物品放入容量为v的背包的最大价值,则有:
dp[i][v]=max{dp[i-1][v-k*C[i]]+k*W[i]},其中0<=k*C[i]<=v,状态方程的建立不难理解
(二)根据状态方程实现代码
有了状态方程,看出i与i-1有关,所以i从1到N遍历,v与v-k*C[i]有关,所以k从0到k*C[i]<=v遍历
 
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. #include <iostream>    
  2. #include <algorithm>    
  3. using namespace std;    
  4.     
  5. #define N 3//物品个数    
  6. #define V 10//背包容量    
  7.     
  8. int C[N+1]={0,10,4,5};  //Ci表示第i件物品的费用   (i从1开始)    
  9. int W[N+1]={0,2,2,1};   //W[i]表示第i件物品的价值    
  10. int dp[N+1][V+1];  
  11.   
  12. int CompletePack(){    
  13.     for(int i=1;i<=N;i++){  
  14.         for(int k=0;k*C[i]<=V;k++)  
  15.             dp[i][V]=max(dp[i][V],dp[i-1][V-k*C[i]]+k*W[i]);  
  16.     }  
  17.     return dp[N][V];  
  18. }  
  19. int main()    
  20. {    
  21.     memset(dp,0,sizeof(dp));  
  22.     cout<<CompletePack();    
  23.     return 0;    
  24. }   

 
(三)优化数据结构
注意到状态方程中i只与i-1有关,可以消去一维,但是如果直接由上面的代码消维是不成的,因为我们用到了上一状态的V-k*C[i],也就是我们在进入下一状态前,这一状态的每一个dp[v],0<=v<=V,必须都是已知的,但是以上代码是对k进行遍历,和v无关,所以要对v进行遍历,那该如何写呢?
从状态方程中找线索,dp[i][v]=max{dp[i-1][v-k*C[i]]+k*W[i]},0<=k*C[i]<=V
我们再把它展开一下看,dp[i][v]=max{dp[i-1][v],dp[i-1][v-C[i]]+W[i],dp[i-1][v-2C[i]]+2W[i]……}
再来看我们需要的v,这个v范围是0<=v<=V,将v的范围带入上式看,我们发现虽然v-C[i],v-2C[i]看起来很繁琐,但是追根究底范围也一定是在0到V之间,
再来看它们之间的关系,发现后一个元素永远比前一个元素多了一个W[i],如果我们事先将dp[v-C[i]]+W[i]的值得到,那么当v增加一个C[i]时,取dp[v-C[i]],这里dp[v-C[i]]就是dp[v-C[i]]+W[i],然后再加上一个W[i],即dp[v-C[i]]+W[i]不就是取得两件i物品所获得的价值了吗!想到这,代码就可以实现了:
 
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. #include <iostream>    
  2. #include <algorithm>    
  3. using namespace std;    
  4.     
  5. #define N 3//物品个数    
  6. #define V 10//背包容量    
  7.     
  8. int C[N+1]={0,10,4,5};  //Ci表示第i件物品的费用   (i从1开始)    
  9. int W[N+1]={0,2,2,1};   //W[i]表示第i件物品的价值    
  10. int dp[V+1];  
  11.   
  12. int CompletePack(){    
  13.     for(int i=1;i<=N;i++){  
  14.         for(int v=C[i];v<=V;v++)  
  15.             dp[v]=max(dp[v],dp[v-C[i]]+W[i]);  
  16.     }  
  17.     return dp[V];  
  18. }  
  19. int main()    
  20. {    
  21.     memset(dp,0,sizeof(dp));  
  22.     cout<<CompletePack();    
  23.     return 0;    
  24. }   

 
发现了吗?代码和01背包问题就内层循环的顺序不同而已,但是实际意义可是大不相同啊
 
(四)深入优化
如果再继续优化也是可以的,如果有两件物品i和j,有C[i]>C[j],W[i]<W[j],也就是i物品所占容量大于j物品,但是i物品的价值却低于j物品,那么i物品就可以不用考虑了
原文地址:https://www.cnblogs.com/bendantuohai/p/4629277.html