动态规划专题 01背包问题详解 HDU 2546 饭卡
我以此题为例,详细分析01背包问题,希望该题能够为大家对01背包问题的理解有所帮助,对这篇博文有什么问题可以向我提问,一同进步^_^
饭卡
Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 14246 Accepted Submission(s):
4952
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。
n=0表示数据结束。
50
5
10
1 2 3 2 1 1 2 3 2 1
50
0
32
这条题目里,我们要先注意要达到最小余额,那么最大的菜价一定是最后要减的,那么我们将这一组饭菜价格按从小到大排序,将最大的那个先放一边,我们接下来就是要把剩下的一些菜价用我们手头的余额减,当然必须要保证减去的金额小于等于sum-5,这样我们才能在最后一次把最大的菜价刷掉。
我们做的转化就是,把除了最大菜价之外,其他的菜价装入一个sum-5 的背包里,看最大能装多少。
首先基于上一篇我们的理论。(很重要!)
【理论讲解】http://www.cnblogs.com/fancy-itlife/p/4393213.html
首先看第一个条件—最优子结构。最大的装入量一定是如果装入第i个或者不装入第i个的两个选择之一。
第二个条件—子问题重叠。当完成一个阶段比如装第i个,我下面做的是对第i-1个进行抉择,你可以发现跟前面的问题一样,装还是不装两个选择之一。这就是所谓的子问题重叠。
第三个条件—边界。这样的选择总归要有个结束的时候,当他到了第一个菜价时,如果它的背包容量也就是余额大于菜价,一定要装进去啊,这才会有可能变得比较大。如果不够的话,那一定是0。至此选择全部结束,然后是递归地返回上一层,直至抉择出正确答案。
第四个条件—子问题独立。装还是不装两个选择,双方的选择不会影响对方。
下面我们就要来考虑一下,第五个条件—备忘录,也就是记忆化搜索,如果这个结果的值已经得到,那么我们把它记录下来,以便后面再出现该值时直接使用。那么对于此问题的独立的小问题的就是执行了前n个菜价的抉择(装或不装),余额还剩m时的最大容量。可以用一个二维数组表示n*m。
那么上面已经详细叙述了该问题的求解方式,用记忆化的方式先来实现一下!
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 1005
using namespace std;
int price[MAXN];
int total[MAXN][MAXN];
int dfs(int m,int k)//利用记忆化搜索实现01背包
{
int s;
if(total[m][k]>=)//如果该值已经被记录了那么直接返回
return total[m][k];
if(k==)//处理边界值
{
if(m>=price[])//如果剩余的额度大于等于该菜价,那么一定返回要将该菜价赋给s
s=price[];
else//如果剩余的额度小于该菜价,那么一定返回0
s=;
}
else if(m>=price[k])//如果此时的额度是大于等于当前的菜价,则是这两种选择之中的一个
s=max((dfs(m-price[k],k-)+price[k]),dfs(m,k-));
else//如果此时的额度是小于当前的菜价,则仅考虑不买这个菜的情况!
s=dfs(m,k-);
total[m][k]=s;//记忆化
return s;
}
int main()
{
int n,i,sum,s;
while(scanf("%d",&n)!=EOF)
{
if(n==)
break;
memset(total,-,sizeof(total));
for(i=;i<=n;i++)
scanf("%d",&price[i]);
scanf("%d",&sum);
if(sum<=)
printf("%d\n",sum);
else
{
sort(price+,price+n+);
s=sum;
sum=dfs(sum-,n-);
sum=s-sum-price[n];
printf("%d\n",sum);
}
}
return ;
}
但其实记忆化搜索的方式,比较适合初学时理解,但是其实它的不足在于递归开销太大,效率不算很高。
接下来我们试着用递推的方式来实现该过程其实我们完全可以将每一个子问题由小到大不断由前面的已解决的问题中推出,比如只有一个菜价时,根据余额和菜价的关系直接就可以得到最大的价值,(这也一定是正确且最大的)到达第二个菜价时,我们抉择的还是装还是不装,装的话,我们要把余额减去第二个菜价看看还剩的钱在前一个选择面前我们能获得的最大金额再加上第二个菜价与不装第二个菜的最大金额比较大小,那么不装第二个菜,那就是第一个菜在这种余额下的最大金额。那么由于第一个阶段是满足最优的,那么你通过两种选择,也就得到了第二个阶段的最有情况。那么往复这样的情况我们就获得了递推式的01背包求解。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 1005
using namespace std;
int price[MAXN];
int total[MAXN][MAXN];
int main()
{
int n,m,i,j,s,sum;
while(scanf("%d",&n)!=EOF)
{
if(n==)
break;
memset(total,,sizeof(total));
for(i=;i<=n;i++)
scanf("%d",&price[i]);
scanf("%d",&sum);
if(sum<=)
printf("%d\n",sum);
else
{
sort(price+,price+n+);
for(i=;i<=sum-;i++)
{
if(i<price[])
total[][i]=;
else
total[][i]=price[];
}
for(i=;i<=n-;i++)//i表示依次选取前n个菜品(标号)
for(j=;j<=sum-;j++)//j表示余额
{
if(j<price[i])
total[i][j]=total[i-][j];
else
total[i][j]=max(total[i-][j-price[i]]+price[i],total[i-][j]);
}
s=;
for(i=;i<=n-;i++)
for(j=;j<=sum-;j++)
{
if(s<total[i][j])
s=total[i][j];
}
//cout<<s<<" "<<price[n]<<endl;
sum=sum-s-price[n];
printf("%d\n",sum);
}
}
return ;
}
那么我们还可以再将空间减少为一维数组,原因是什么呢,代码的注释里详细的解释了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 1005
using namespace std;
int price[MAXN];
int total[MAXN];
int main()
{
int n,m,i,j,s,sum;
while(scanf("%d",&n)!=EOF)
{
if(n==)
break;
memset(total,,sizeof(total));
for(i=;i<=n;i++)
scanf("%d",&price[i]);
scanf("%d",&sum);
if(sum<=)
printf("%d\n",sum);
else
{
sort(price+,price+n+);
//为什么只要用到一维数组,因为它的第二维只跟前一阶段有关,
//那么用一维数组就可以保存一个阶段的值,下一个阶段用上一个阶段来更新
for(i=;i<=n-;i++)//前n个阶段
for(j=sum-;j>=;j--)//表示此时该阶段如果为有j余额
{
if(j>=price[i])
total[j]=max(total[j-price[i]]+price[i],total[j]);
/*为什么需要逆序因为逆序可以带来的正确性是不言而喻的
我需要将前一阶段的j-price[i]余额的最大的消费获取到,
如果正向的话,我在求取一些余额较大的值时可能获得了该阶段
的j-price[i]的最大的消费额,因为小的余额是先更新的。
*/
}
s=;
for(j=;j<=sum-;j++)
{
if(s<total[j])
s=total[j];
}
sum=sum-s-price[n];
printf("%d\n",sum);
}
}
return ;
}
再看看这三题的时间空间效率对比

自己也是才接触这类动态规划问题,也希望此篇博文对大家学习01背包有所帮助!
动态规划专题 01背包问题详解 HDU 2546 饭卡的更多相关文章
- HDOJ(HDU).2546 饭卡(DP 01背包)
HDOJ(HDU).2546 饭卡(DP 01背包) 题意分析 首先要对钱数小于5的时候特别处理,直接输出0.若钱数大于5,所有菜按价格排序,背包容量为钱数-5,对除去价格最贵的所有菜做01背包.因为 ...
- HDU 2546 饭卡(01背包)
题目代号:HDU 2546 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2546 饭卡 Time Limit: 5000/1000 MS (Java/ ...
- HDU 2546 饭卡 (01背包问题)
题意:中文的吧,飘过~ 析:学过DP的都应该感觉到是动态规划吧,就是一个01背包问题,不同的是,这个题又加入一些新的条件,就是不满5元不能消费,过了5元即使超了也行(这个学校真不错,都可以预支),最后 ...
- HDU 2546 饭卡(01背包裸题)
饭卡 Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...
- HDU 2546 饭卡(01 背包)
链接:http://acm.hdu.edu.cn/showproblem.php?pid=2546 思路:需要首先处理一下的的01背包,当饭卡余额大于等于5时,是什么都能买的,所以题目要饭卡余额最小, ...
- hdu 2546 饭卡【01背包】
题目链接:https://vjudge.net/contest/103424#problem/C 饭卡 Time Limit: 5000/ ...
- HDU 2546 饭卡(0-1背包)
http://acm.hdu.edu.cn/showproblem.php?pid=2546 题意: 电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额.如果购买一个商品之前,卡上的剩余金 ...
- 【01背包】HDU 2546 饭卡
Time Limit : 5000/1000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other) Total Submission(s) ...
- hdu 2546 饭卡 (01背包)
Problem Description 电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额.如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负) ...
随机推荐
- Win7如何自定义鼠标右键菜单 添加新建EXCEL文档
鼠标右键添加新建EXCEL文档.reg Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\.xls] "Content Type ...
- PS 如何用PS制作GIF图像
首先我们准备好要依次播放的图片(这里使用的是CS的光标缩放,只有两张图) 然后在窗口中打开动画,则下方会出现动画的面板. 点击图层按钮可以添加一帧,我们让第一帧显示为大图片,第二帧为小图片.还可以设置 ...
- C 标准库 - <stdlib.h>
C 标准库 - <stdlib.h> 简介 stdlib .h 头文件定义了四个变量类型.一些宏和各种通用工具函数. 库变量 下面是头文件 stdlib.h 中定义的变量类型: 序号 变量 ...
- discuz X3.1+Apache2.2+php-5.2.17+mysql5.6.14+Discuz_X3.1
discuz X3.1+Apache2.2.25+php-5.2.17+mysql5.6.14+Discuz_X3.1 一.准备 1.httpd-2.2.25-win32-x86-no_ssl.msi ...
- Java集合01----ArrayList的遍历方式及应用
Java集合01----ArrayList的遍历方式及应用 前面已经学习了ArrayList的源代码,为了学以 ...
- KMP算法模式匹配
转载请注明出处 http://blog.csdn.net/pony_maggie/article/details/37832707 作者:小马 在一个长串中查找一个子串是较经常使用的操作.各种信息检索 ...
- lua 字符串处理
匹配模式(pattern) . 任何单个字符 %a 任何字母 %c 任何控制字符 %d 任何数字 %g 任何除空白符外的可打印字符 %l 所有小写字母 %p 所有标点符号 %s 所有空白字符 %u 所 ...
- 九度OJ 1127:简单密码 (翻译)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:1218 解决:721 题目描述: Julius Caesar曾经使用过一种很简单的密码. 对于明文中的每个字符,将它用它字母表中后5位对应的 ...
- 阿里Java开发手册学习 3 MYSQL规约
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...
- SpringBoot-(9)-MyBatis 操作数据库
这里仅仅以插入数据为例: 一, 创建基于MyBatis的项目 具体流程参考之前帖 二,创建Mapper接口 public interface AccountMapper { @Insert(" ...