• 完全背包--动态规划


    题目:
    有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物品就可以不用考虑了
  • 相关阅读:
    如何将 Web 框架迁移到 Serverless
    Serverless + CVM 实战
    Serverless Registry 设计解读与实战
    使用 ServerLess 实现云原生
    腾讯云 Serverless 技术在「老司机汽车 app」的落地实践
    LeetCode 树 103. 二叉树的锯齿形层次遍历(双端队列 DFS的空间复杂度)
    LeetCode 树 116. 填充每个节点的下一个右侧节点指针(层序遍历 分层)
    LeetCode 树 230. 二叉搜索树中第K小的元素(二叉搜索树 中序遍历 剪枝)
    LeetCode 树 236. 二叉树的最近公共祖先(递归 深度优先搜索)
    LeetCode 树 102. 二叉树的层序遍历(广度优先搜索 深度优先搜索 队列)
  • 原文地址:https://www.cnblogs.com/bendantuohai/p/4629277.html
Copyright © 2020-2023  润新知