我以此题为例,详细分析01背包问题,希望该题能够为大家对01背包问题的理解有所帮助,对这篇博文有什么问题可以向我提问,一同进步^_^

饭卡

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 14246    Accepted Submission(s):
4952

Problem Description
电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。
 
Input
多组数据。对于每组数据:
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。

n=0表示数据结束。

 
Output
对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。
 
Sample Input
1
50
5
10
1 2 3 2 1 1 2 3 2 1
50
0
 
Sample Output
-45
32
 
Source

这条题目里,我们要先注意要达到最小余额,那么最大的菜价一定是最后要减的,那么我们将这一组饭菜价格按从小到大排序,将最大的那个先放一边,我们接下来就是要把剩下的一些菜价用我们手头的余额减,当然必须要保证减去的金额小于等于sum-5,这样我们才能在最后一次把最大的菜价刷掉。

我们做的转化就是,把除了最大菜价之外,其他的菜价装入一个sum-5 的背包里,看最大能装多少。

首先基于上一篇我们的理论。(很重要!)

【理论讲解】http://www.cnblogs.com/fancy-itlife/p/4393213.html

首先看第一个条件—最优子结构。最大的装入量一定是如果装入第i个或者不装入第i个的两个选择之一。

第二个条件—子问题重叠。当完成一个阶段比如装第i个,我下面做的是对i-1个进行抉择,你可以发现跟前面的问题一样,装还是不装两个选择之一。这就是所谓的子问题重叠。

第三个条件—边界。这样的选择总归要有个结束的时候,当他到了第一个菜价时,如果它的背包容量也就是余额大于菜价,一定要装进去啊,这才会有可能变得比较大。如果不够的话,那一定是0。至此选择全部结束,然后是递归地返回上一层,直至抉择出正确答案。

第四个条件—子问题独立。装还是不装两个选择,双方的选择不会影响对方。

下面我们就要来考虑一下,第五个条件—备忘录,也就是记忆化搜索,如果这个结果的值已经得到,那么我们把它记录下来,以便后面再出现该值时直接使用。那么对于此问题的独立的小问题的就是执行了前n个菜价的抉择(装或不装),余额还剩m时的最大容量。可以用一个二维数组表示n*m

那么上面已经详细叙述了该问题的求解方式,用记忆化的方式先来实现一下!

 代码

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 1005
  6. using namespace std;
  7. int price[MAXN];
  8. int total[MAXN][MAXN];
  9. int dfs(int m,int k)//利用记忆化搜索实现01背包
  10. {
  11. int s;
  12. if(total[m][k]>=)//如果该值已经被记录了那么直接返回
  13. return total[m][k];
  14. if(k==)//处理边界值
  15. {
  16. if(m>=price[])//如果剩余的额度大于等于该菜价,那么一定返回要将该菜价赋给s
  17. s=price[];
  18. else//如果剩余的额度小于该菜价,那么一定返回0
  19. s=;
  20. }
  21. else if(m>=price[k])//如果此时的额度是大于等于当前的菜价,则是这两种选择之中的一个
  22. s=max((dfs(m-price[k],k-)+price[k]),dfs(m,k-));
  23. else//如果此时的额度是小于当前的菜价,则仅考虑不买这个菜的情况!
  24. s=dfs(m,k-);
  25. total[m][k]=s;//记忆化
  26. return s;
  27. }
  28. int main()
  29. {
  30. int n,i,sum,s;
  31. while(scanf("%d",&n)!=EOF)
  32. {
  33. if(n==)
  34. break;
  35. memset(total,-,sizeof(total));
  36. for(i=;i<=n;i++)
  37. scanf("%d",&price[i]);
  38. scanf("%d",&sum);
  39. if(sum<=)
  40. printf("%d\n",sum);
  41. else
  42. {
  43. sort(price+,price+n+);
  44. s=sum;
  45. sum=dfs(sum-,n-);
  46. sum=s-sum-price[n];
  47. printf("%d\n",sum);
  48. }
  49. }
  50. return ;
  51. }

但其实记忆化搜索的方式,比较适合初学时理解,但是其实它的不足在于递归开销太大,效率不算很高。

接下来我们试着用递推的方式来实现该过程其实我们完全可以将每一个子问题由小到大不断由前面的已解决的问题中推出,比如只有一个菜价时,根据余额和菜价的关系直接就可以得到最大的价值,(这也一定是正确且最大的)到达第二个菜价时,我们抉择的还是装还是不装,装的话,我们要把余额减去第二个菜价看看还剩的钱在前一个选择面前我们能获得的最大金额再加上第二个菜价与不装第二个菜的最大金额比较大小,那么不装第二个菜,那就是第一个菜在这种余额下的最大金额。那么由于第一个阶段是满足最优的,那么你通过两种选择,也就得到了第二个阶段的最有情况。那么往复这样的情况我们就获得了递推式的01背包求解。

 代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 1005
  6. using namespace std;
  7. int price[MAXN];
  8. int total[MAXN][MAXN];
  9. int main()
  10. {
  11. int n,m,i,j,s,sum;
  12. while(scanf("%d",&n)!=EOF)
  13. {
  14. if(n==)
  15. break;
  16. memset(total,,sizeof(total));
  17. for(i=;i<=n;i++)
  18. scanf("%d",&price[i]);
  19. scanf("%d",&sum);
  20. if(sum<=)
  21. printf("%d\n",sum);
  22. else
  23. {
  24. sort(price+,price+n+);
  25. for(i=;i<=sum-;i++)
  26. {
  27. if(i<price[])
  28. total[][i]=;
  29. else
  30. total[][i]=price[];
  31. }
  32. for(i=;i<=n-;i++)//i表示依次选取前n个菜品(标号)
  33. for(j=;j<=sum-;j++)//j表示余额
  34. {
  35. if(j<price[i])
  36. total[i][j]=total[i-][j];
  37. else
  38. total[i][j]=max(total[i-][j-price[i]]+price[i],total[i-][j]);
  39. }
  40. s=;
  41. for(i=;i<=n-;i++)
  42. for(j=;j<=sum-;j++)
  43. {
  44. if(s<total[i][j])
  45. s=total[i][j];
  46. }
  47. //cout<<s<<" "<<price[n]<<endl;
  48. sum=sum-s-price[n];
  49. printf("%d\n",sum);
  50. }
  51. }
  52. return ;
  53. }

那么我们还可以再将空间减少为一维数组,原因是什么呢,代码的注释里详细的解释了。

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 1005
  6. using namespace std;
  7. int price[MAXN];
  8. int total[MAXN];
  9. int main()
  10. {
  11. int n,m,i,j,s,sum;
  12. while(scanf("%d",&n)!=EOF)
  13. {
  14. if(n==)
  15. break;
  16. memset(total,,sizeof(total));
  17. for(i=;i<=n;i++)
  18. scanf("%d",&price[i]);
  19. scanf("%d",&sum);
  20. if(sum<=)
  21. printf("%d\n",sum);
  22. else
  23. {
  24. sort(price+,price+n+);
  25. //为什么只要用到一维数组,因为它的第二维只跟前一阶段有关,
  26. //那么用一维数组就可以保存一个阶段的值,下一个阶段用上一个阶段来更新
  27. for(i=;i<=n-;i++)//前n个阶段
  28. for(j=sum-;j>=;j--)//表示此时该阶段如果为有j余额
  29. {
  30. if(j>=price[i])
  31. total[j]=max(total[j-price[i]]+price[i],total[j]);
  32. /*为什么需要逆序因为逆序可以带来的正确性是不言而喻的
  33. 我需要将前一阶段的j-price[i]余额的最大的消费获取到,
  34. 如果正向的话,我在求取一些余额较大的值时可能获得了该阶段
  35. 的j-price[i]的最大的消费额,因为小的余额是先更新的。
  36. */
  37. }
  38. s=;
  39. for(j=;j<=sum-;j++)
  40. {
  41. if(s<total[j])
  42. s=total[j];
  43. }
  44. sum=sum-s-price[n];
  45. printf("%d\n",sum);
  46. }
  47. }
  48. return ;
  49. }

再看看这三题的时间空间效率对比

自己也是才接触这类动态规划问题,也希望此篇博文对大家学习01背包有所帮助!

动态规划专题 01背包问题详解 HDU 2546 饭卡的更多相关文章

  1. HDOJ(HDU).2546 饭卡(DP 01背包)

    HDOJ(HDU).2546 饭卡(DP 01背包) 题意分析 首先要对钱数小于5的时候特别处理,直接输出0.若钱数大于5,所有菜按价格排序,背包容量为钱数-5,对除去价格最贵的所有菜做01背包.因为 ...

  2. HDU 2546 饭卡(01背包)

    题目代号:HDU 2546 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2546 饭卡 Time Limit: 5000/1000 MS (Java/ ...

  3. HDU 2546 饭卡 (01背包问题)

    题意:中文的吧,飘过~ 析:学过DP的都应该感觉到是动态规划吧,就是一个01背包问题,不同的是,这个题又加入一些新的条件,就是不满5元不能消费,过了5元即使超了也行(这个学校真不错,都可以预支),最后 ...

  4. HDU 2546 饭卡(01背包裸题)

    饭卡 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...

  5. HDU 2546 饭卡(01 背包)

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=2546 思路:需要首先处理一下的的01背包,当饭卡余额大于等于5时,是什么都能买的,所以题目要饭卡余额最小, ...

  6. hdu 2546 饭卡【01背包】

    题目链接:https://vjudge.net/contest/103424#problem/C 饭卡                                Time Limit: 5000/ ...

  7. HDU 2546 饭卡(0-1背包)

    http://acm.hdu.edu.cn/showproblem.php?pid=2546 题意: 电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额.如果购买一个商品之前,卡上的剩余金 ...

  8. 【01背包】HDU 2546 饭卡

    Time Limit : 5000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) ...

  9. hdu 2546 饭卡 (01背包)

    Problem Description 电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额.如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负) ...

随机推荐

  1. .net网站上传图片换电脑不显示 当不用网站的IP地址访问图片,只用相对路径访问时,在发布网站的时候,将上传图片的目标文件夹,包含在项目中再发布即可。

    .net网站上传图片换电脑不显示 当不用网站的IP地址访问图片,只用相对路径访问时,在发布网站的时候,将上传图片的目标文件夹,包含在项目中再发布即可.

  2. [笔记] sed and awk

    / awk程序的典型示例是将数据转换成格式化的报表,当数据拥有某种结构时就能最好的体现awk的好处:可以使用awk脚本对数据的列重新排序,甚至可以将列变成行以及将行变成列:awk的功能将文本编辑的思想 ...

  3. HDU1009_FatMouse&#39; Trade【贪心】【水题】

    FatMouse' Trade Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  4. 关于 angular cookie 设置的坑

    初识Angular,才知道掉进了这么一个各种大坑的坑. 先说下对于$cookie.put 这几个方法,只有1.4以上版本才可以用,其余低于版本请使用 $cookieStore: 下面举例下使用方法: ...

  5. SVProgressHUD 用法一

    SVProgressHUD 用法一  SVProgressHUD 是一个第三方的控件,是一个弹出提示层,用来提示 网络加载 或 提示对错,看下面图,你就明白了:     那么,SVProgressHU ...

  6. Qt 5.5.0 Windows环境搭建

    1)訪问官方站点:http://www.qt.io/download-open-source/ 2)选择离线安装包 3)选择 Windows 离线安装包(32 位或 64 位都可用,Windows 6 ...

  7. 【BZOJ1845】[Cqoi2005] 三角形面积并 几何+扫描线

    [BZOJ1845][Cqoi2005] 三角形面积并 Description 给出n个三角形,求它们并的面积. Input 第一行为n(N < = 100), 即三角形的个数 以下n行,每行6 ...

  8. innodb的锁和高并发

    1 innodb的锁 1.1 s锁,即读锁,即share锁 1.2 x锁,即写锁,排他锁 1.3 s锁和x锁之间的关系 多个读锁可以共存,但是读锁不可以和写锁共存.写锁和写锁不可以共存. 1.4 间隙 ...

  9. Cocos2d-JS开发中的一些小技巧

    1.获取URL中的请求参数的值----此方法接收参数名 function getQueryString(name) { var reg = new RegExp("(^|&)&quo ...

  10. Could not find com.android.tools.lint:lint-gradle:26.1.2.

    allprojects { repositories { flatDir { dirs 'libs' } jcenter() google() }}