每日一题(7)——0-1背包问题(动态规划)

问题描述:

一个小偷去偷金库(这个小偷比较NB~),带了一个能承重N的背包,金库里放了不同品质的金砖,以(重量,价值)形式给出,问小偷怎样拿,获利最大?

输入:

第一行:金砖数目, 背包承重能力;

其他行:金砖重量, 金砖价值

输出:

带走金砖数目;带走最大价值;金砖序号

input:

3 50 
10 60
20 100
30 120

output:

2

220  

2 3

 

这道题是典型的动态规划,跟公正陪审团问题的解法相似:

利用

f[j][k]:j=带走金砖数目,k=金砖重量,f[j][k]:带走金砖数目=j,且金砖重量=k时的价值;

 

分几层循环,

最外层遍历所有的数量j:[0,n)

第二层遍历所有可能的重量:k:[0,M] 判断f[j][k]>=0来决定是否进入循环(根据j 来判断,根据j 计算j+1)

内层遍历所有的金砖i:[1,n]

if( f[j][k]+v[i] > f[j+1][k+w[i]]&&k+w[i]<=M)

{

检测i是否出现过,若没有出现过 f[j+1][k+w[i]] = f[j][k]+v[i];(初始条件是f[0][0]=0;)

}

 

 

  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #include<iostream>  
  4. #include<string.h>  
  5. #include <set>  
  6.   
  7. using namespace std;  
  8.   
  9. int f[30][1000];  
  10.   
  11. int Path[30][1000];  
  12.   
  13. int w[300];//质量;  
  14. int v[300]; //价值;  
  15.   
  16. set<int> index;//存放最终方案  
  17.   
  18. int main()  
  19. {  
  20.     int i,j,k;  
  21.     int t1,t2;  
  22.     int n,M;//n为数量,m为最大重量;  
  23.   
  24.   
  25.     while(scanf("%d %d",&n,&M))  
  26.     {  
  27.         if(n==0&&M==0)break;  
  28.         for(i=1;i<=n;i++)  
  29.             scanf("%d %d",&w[i],&v[i]);  
  30.         memset(f,-1,sizeof(f));  
  31.         memset(Path,0,sizeof(Path));  
  32.           
  33.         f[0][0]=0;//初始化条件,根据f[0][0]推以后的结果  
  34.   
  35.         for(j=0;j<n;j++)                 //每次循环选出第j个物品,最多n个;  
  36.         {  
  37.             for(k=0; k <=M; k++) //可能的重量为[0,M];   
  38.                 if(f[j][k]>=0)               //方案f[j,k]可行,从f[0][0]开始;   
  39.                 {  
  40.                     for(i=1; i<=n; i++)  
  41.                         if( f[j][k]+v[i] > f[j+1][k+w[i]]&&k+w[i]<=M) //若f[j+1][k+w[i]]已经有值,则保存较大的一个;  
  42.                         {  
  43.                             t1=j;t2=k;  
  44.                             while(t1>0&&Path[t1][t2]!=i)//验证i是否在前面出现过;  
  45.                             {  
  46.                                 t2-=w[Path[t1][t2]];            //减前一个元素的重量;  
  47.                                 t1--;  
  48.                             }  
  49.                             if(t1==0)//若i未出现过;  
  50.                             {  
  51.                                 f[j+1][k+w[i]] = f[j][k]+v[i];  
  52.                                 Path[j+1][k+w[i]]=i;  
  53.                             }           
  54.                         }      
  55.                 }      
  56.         }  
  57.   
  58.         int maxNum=0,maxV=0,maxW=0;  
  59.         for (int i=1; i<=n; i++)  
  60.         {  
  61.             for (int j=0; j<=M; j++)//注意必须 =M  
  62.             {  
  63.                 if(maxV<f[i][j])   
  64.                 {  
  65.                     maxV=f[i][j];  
  66.                     maxNum=i;  
  67.                     maxW=j;  
  68.                 }  
  69.             }  
  70.         }  
  71.         cout<<"MAX Value: "<<maxV<<endl;  
  72.         cout<<"MAX num: "<<maxNum<<endl;  
  73.   
  74.         index.clear();  
  75.         for (int i=0; i<maxNum; i++)  
  76.         {  
  77.             int id=Path[maxNum-i][maxW];//若直接输出,由于递归,则是乱序的;  
  78.             index.insert(id);  
  79.             maxW -= w[id];  
  80.         }  
  81.         cout<<endl;  
  82.         for(set<int>::iterator iter=index.begin(); iter!=index.end(); iter++) cout<<*iter<<" ";  
  83.     }    
  84.     return 0;     
  85. }  


0-1背包问题和陪审团问题都是属于动态规划中的同一类问题,这种问题比较难做(我比较菜鸟,这是我的感觉啊~)

这类问题关键要换个思路,要给最优解添加约束条件:f[j][k],k金砖重量就是约束条件,要根据约束条件k进行迭代k[0,M],迭代的过程要首先判断是否符合题意:if(f[j][k]>=0),

最内层要不断的组合没有计算过的元素,根据已知推未知f[j+1][k+w[i]] = f[j][k]+v[i];

 

另外部分背包问题可以用贪心算法解决(相当于小偷偷的是金沙)背包问题还有很多种,这都以后再讲。

 

续……

读完背包九讲发现我解题的方法略复杂,其实利用一维数组就可以实现动态规划,

根据状态转移方程:f[w]=max{f[w], f[w-c]+v}

f[w]表示在重量为w时的最优解,


参考网上一位大牛重新写的代码:

  1. #include <iostream>  
  2.   
  3. using namespace std;  
  4.   
  5. const int nMax=400;     //待选物品数;  
  6. const int mMax=10000;  //最大载重;  
  7.   
  8. struct{  
  9.     int wei,val;  
  10. }node[nMax];  
  11.   
  12. int main()  
  13. {  
  14.     int m, n, i ,w ,dp[mMax];  
  15.     while(cin>>n>>m)//n为待选物品数量,m为最大载重量;  
  16.     {  
  17.         if(m==0&&n==0) break;  
  18.         for (i=1; i<=n;i++)  
  19.             cin>>node[i].wei>>node[i].val;  
  20.         memset(dp, 0, (m+1)*sizeof(int));  
  21.   
  22.         for (i=1; i<=n; i++)  
  23.             for ( w=m; w>=node[i].wei; w-- )  
  24.                 if ( dp[w] < dp[w - node[i].wei] + node[i].val )  
  25.                     dp[w] = dp[w - node[i].wei] + node[i].val;  
  26.   
  27.         cout<<dp[m]<<endl;  
  28.     }  
  29. }  


DynamicProgram问题也真是博大精深呐~ 题在而不在多~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值