动态规划专题 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元,就一定可以购买成功(即使购买后卡上余额为负) ...
随机推荐
- SolidEdge 工程图中如何标注尺寸公差
1 先标注基准框,输入基准符号(A B C之类的) 2 点击特征控制,设置马上要标注的特征和公差(可以保存为模板) 3 直接点击要标注的元素即可 4 没有基准的形位公差标注也一样
- 手把手教你将本地项目文件上传至github
相信大家都听过Git(分布式版本号控制系统)和github吧.没听过也没关系(Google一下),反正以后要去公司肯定会听过. 我是在今年年初才接触Git.之后就一发不可收拾.仅仅要有比較好的项目就G ...
- 【每日Scrum】第八天(4.29) TD学生助手Sprint2
站立会议 组员 今天 签到 刘铸辉 (组长) 绩效考核 Y 刘静 测试用例书写 测试bug报告 测试详细报告 Y 解凤娇 Y 王洪叶 项目可行性报告 项目开发计划书 需求分析(已完成并发布) Y 胡宝 ...
- [RFC] Simplifying kernel configuration for distro issues
http://lwn.net/Articles/507276/ From: Linus Torvalds <torvalds-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b-A ...
- thttpd源代码解析 定时器模块
thttpd源代码解析 定时器模块 thttpd是很轻量级的httpserver,可运行文件仅50kB.名称中的第一个t表示tiny, turbo, 或throttling 与lighttpd.mem ...
- activity fragment 转场动画
http://www.cnblogs.com/avenwu/p/3372736.html v4 fragment fragmentTransaction.setCustomAnimations(R.a ...
- 一个兼容性比较好的图片左右滚动的js
下载地址:http://www.cnblogs.com/RightDear/admin/Files.aspx 文件:shhds.rar
- PHP中的面向对象 中的类(class)
2.11 上午讲的是面向对象中的类(class),一个非常抽象的概念, 类里面成员的定义有 public$abc; private$abc(私有变量): protect $abc(受保护的变量): 下 ...
- 九度OJ 1141:Financial Management (财务管理) (平均数)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:939 解决:489 题目描述: Larry graduated this year and finally has a job. He's ...
- jvm -Xms -Xmx
1 -Xms -X表示这是一个“-X”参数,m即memory,s即start,这个是jvm初始可以使用的整个堆的大小. 2 -Xmx x表示max,jvm最大可以使用的整个堆的大小. 3 oracle ...