动态规划之01背包问题讲解

给大家附上一个题目吧,便于理解

ctest有n个苹果,要将它放入容量为v的背包。给出第i个苹果的大小和价钱,求出能放入背包的苹果的总价钱最大值。

输入:每组测试数据第一行为2个正整数,分别代表苹果的个数n和背包的容量v

接下来的n行,每行2个正整数,用空格隔开,分别代表苹果的大小c和价钱w  

01背包其实就是遍历所有可能情况  然后取最优的结果   和搜索差不多 不过比搜索快

0 1 背包的方程为dp[i]=max(dp[i-c[j]])+w[j],dp[i]) 

i:表示当前的背包容量

j:是苹果的序号

dp[i]:是容量为i的背包能放的最大价值 

c[j]:序号为j的苹果的大小

w[j]:序号为j的苹果的价值 

这个方程翻译成白话文就是   容量为i的背包的最大价值=(当前背包容量-序号为j的苹果的大小)的最大价值+序号为j的苹果的价值  和 容量为i的背包的当前价值 之间的最大值 

说着比较绕口。慢慢理解

说白了  ,其实就是取与不取的问题 ,如果取了  那么取后的价值要大于我没有取之前的价值 否则我就不要你  (因为同样大小的背包我要装价值更大的啊)

对着这道题举个例子吧

5     10

1      9

4      4

2      6

5      5

10    8

你能根据自己的想法填下表吗  看结果是否和我的一样

 

这个结果其实就是根据0-1背包的思想得到的 ,如果你能填 证明你已经入门了

我首先附上0-1背包的代码 

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. for(int i=0;i<n;i++)  
  2. {  
  3.     for(int j=v;j>=0;j--)  
  4.     {  
  5.         if(j>=c[i])  
  6.         dp[j]=max(dp[j-c[i]]+w[i],dp[j]);  
  7.     }  
  8. }  


你肯定有几个问题?

1.为什么外层循环是苹果的数目 而不是背包的容量

答:因为每个苹果只能选择一次  如果背包容量在外 

2.外循环是对苹果的遍历 那么内循环为什么是v-》0而不是 从0-》v

这个问题我也迷惑了好久   我们仔细看看动态方程dp[i]=max(dp[i-c[j]])+w[j],dp[i])    我们首先假设  有一个苹果大小为1   价值为9

那么dp[1]=max(dp[1-1]+9,dp[1])=9  是正常的 dp[2]=max(dp[2-1]+9,dp[2])=18..dp[3]=27,dp[4]=36等等  发现问题了吧

所以内循环从0-》v是错误的    如果从v-》0就行了  因为在每个苹果循环的时候 我们要保证当前已经遍历的背包对我未遍历的背包没有影响

看完了这些 分析上面的截图吧

首先是对苹果大小为1  价值为9     背包容量为10.....1  最大价值都为9  背包容量为0  最大价值为0

苹果大小为4 价值为4      背包容量为10.....5的价值变为13  为什么呢  还是动态规划方程dp[i]=max(dp[i-c[j]])+w[j],dp[i])    分析一个为5的吧  dp[5]=max(d[4]+4,dp[5])=9+4=13    而在背包容量为4的时候 我们发现价值还是9  没有变化因为这两个苹果大小4+1=5>4

由于我们对每个苹果遍历后 都是当前苹果个数的最优结果   所以当我们遍历完最后一个苹果 那么结果也就是最优化的

就分析这么多吧 

这道题的传送门 和AC代码

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1.    
  2. #include <stdio.h>  
  3. #include <string.h>  
  4. int main()  
  5. {  
  6.     int n,v,max,c[1005],w[1005],dp[1005];  
  7.     while(scanf("%d %d",&n,&v)!=EOF)  
  8.     {  
  9.         if(n==0&&v==0)  
  10.         break;  
  11.         memset(dp,0,sizeof(dp));  
  12.         for(int i=0;i<n;i++)  
  13.         scanf("%d %d",&c[i],&w[i]);  
  14.         max=0;  
  15.         for(int i=0;i<n;i++)  
  16.         for(int j=v;j>=c[i];j--)  
  17.         {  
  18.         //  dp[j]=dp[j];  
  19.             if(dp[j]<dp[j-c[i]]+w[i])  
  20.             dp[j]=dp[j-c[i]]+w[i];  
  21.             if(max<dp[j])  
  22.             max=dp[j];  
  23.         }  
  24.         printf("%d\n",max);  
  25.     }  
  26.     return 0;  
  27. }          
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值