01背包问题浅析
2016-02-02 11:09
211 查看
①例题一:
简单背包问题
Time Limit: 1000MS Memory Limit: 65535KB
Submissions: 2217 Accepted: 408
Description
设有一个背包可以放入的物品重量为S,现有n件物品,重量分别是w1,w2,w3,…wn。
问能否从这n件物品中选择若干件放入背包中,使得放入的重量之和正好为S。
如果有满足条件的选择,则此背包有解,否则此背包问题无解。
Input输入数据有多行,包括放入的物品重量为s,物品的件数n,以及每件物品的重量(输入数据均为正整数)
多组测试数据。
Output对于每个测试实例,若满足条件则输出“YES”,若不满足则输出“NO“
Sample Input
20 5
1 3 5 7 9
Sample Output
YES
上述问题便是一个简单的01背包问题,那我们该如何求解比较好呢?我们不妨先用比较朴素的方法
当然,我们也可以使用贪心来解决这个问题
我们通过来一步步求解最优解,先达到局部最优,再来求整体最优。而局部最优的比较方法就是(价值/重量):
上面的方法也许可以勉强过了这个题,但这终究不是正解,对于刚开始提到的方法,搜索层次是n,并且每一层的搜索都需要两次分支,最坏就需要O(2^n)的时间,当n过大时就没办法求解,到底该怎么办,我们不妨来看一下搜索树:
![](http://img.blog.csdn.net/20160202111330266?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
从搜索树上我们可以看到,rec(3,2)为参数调用了两次,本来结果相同,却再次计算,那假如我们把结果保存,下次要用时直接调用,那么程序效率将大大提高。
接下来,我们将用动态规划的方法求解,实质就是要找出那个规划方程。
首先,我们弄一个记忆化数组dp[maxn+1][maxn+1],表示从第i个物品挑选总重小于j时,总价值的最大值,于是我们有如下递推式:
![](http://img.blog.csdn.net/20160202111545954?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
于是dp解决方法的代码如下:
下面是一个例题:
Description辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
Input输入的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
Output输出包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
Sample Input
70 3
71 100
69 1
1 2
Sample Output
3
代码如下:
简单背包问题
Time Limit: 1000MS Memory Limit: 65535KB
Submissions: 2217 Accepted: 408
Description
设有一个背包可以放入的物品重量为S,现有n件物品,重量分别是w1,w2,w3,…wn。
问能否从这n件物品中选择若干件放入背包中,使得放入的重量之和正好为S。
如果有满足条件的选择,则此背包有解,否则此背包问题无解。
Input输入数据有多行,包括放入的物品重量为s,物品的件数n,以及每件物品的重量(输入数据均为正整数)
多组测试数据。
Output对于每个测试实例,若满足条件则输出“YES”,若不满足则输出“NO“
Sample Input
20 5
1 3 5 7 9
Sample Output
YES
上述问题便是一个简单的01背包问题,那我们该如何求解比较好呢?我们不妨先用比较朴素的方法
int n,w; int W[maxn],V[maxn]; int rec(int i,int j) { if(i==n)//没有物品可供选择了 res= 0; else if(j < W[i])//这个物品过重而不能挑选 res = rec(i+1,j); else//选择或者不选择 res = max(rec(i+1,j),rec(i+1,j-W[i])+V[i]); return res; } void main() { printf("%d\n",rec(0,w)); }
当然,我们也可以使用贪心来解决这个问题
我们通过来一步步求解最优解,先达到局部最优,再来求整体最优。而局部最优的比较方法就是(价值/重量):
#include<iostream> #include<algorithm> using namespace std; struct good//表示物品的结构体 { double p;//价值 double w;//重量 double r;//价值与重量的比 }; good a[2000]; bool bigger(good a,good b) { if(a.r==b.r)return a.w<b.w; else return a.r>b.r; } int main() { double s,value,m; int i,n; cin>>m>>n;//读入包的容量和物品个数 for (i=0;i<n;i++) { cin>>a[i].w>>a[i].p; a[i].r=a[i].p/a[i].w; } sort(a,a+n,bigger);//调用sort排序函数,按照价值与重量比和质量排序贪心 s=0;//包内现存货品的重量 value=0;//包内现存货品总价值 for (i=0;i<n;i++) if(s+a[i].w<=m) { value+=a[i].p; s+=a[i].w; } cout<<"The total value is "<<value<<endl;//输出结果 return 0; }
上面的方法也许可以勉强过了这个题,但这终究不是正解,对于刚开始提到的方法,搜索层次是n,并且每一层的搜索都需要两次分支,最坏就需要O(2^n)的时间,当n过大时就没办法求解,到底该怎么办,我们不妨来看一下搜索树:
从搜索树上我们可以看到,rec(3,2)为参数调用了两次,本来结果相同,却再次计算,那假如我们把结果保存,下次要用时直接调用,那么程序效率将大大提高。
接下来,我们将用动态规划的方法求解,实质就是要找出那个规划方程。
首先,我们弄一个记忆化数组dp[maxn+1][maxn+1],表示从第i个物品挑选总重小于j时,总价值的最大值,于是我们有如下递推式:
于是dp解决方法的代码如下:
int dp[maxn+1][maxn+1]; void solve() { for(int i=n-1;i>=0;i--) { for(int j=0;j<=w;j++) { if(j<w[i]) dp[i][j] = dp[i+1][j]; else sp[i][j] = max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]); } } printf("%d\n",dp[0][w]); }
下面是一个例题:
Description辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
Input输入的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
Output输出包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
Sample Input
70 3
71 100
69 1
1 2
Sample Output
3
代码如下:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int dp[110][110];//从第i个物品挑选时间小于j的草药最大价值 int t,m; int T[110]; int W[110]; int a,b; int main() { scanf("%d %d",&t,&m); for(int i=0;i<m;i++) { scanf("%d %d",&T[i],&W[i]); } memset(dp,0,sizeof(dp)); for(int i=m-1;i>=0;i--) { for(int j=0;j<=t;j++) { if(j<T[i]) dp[i][j] = dp[i+1][j]; else dp[i][j] = max(dp[i+1][j],dp[i+1][j-T[i]]+W[i]); } } printf("%d\n",dp[0][t]); return 0; }
相关文章推荐
- 高仿QQ6.0之侧滑删除
- 【ActiveMQ】持久化消息队列的三种方式
- Intellij里代码模板的时间格式调整
- 【Github教程】史上最全github使用方法:github入门到精通
- 启动rabbitmq报错:/usr/lib/rabbitmq/bin/rabbitmq-server: line 80: /var/lib/rabbitmq/mnesia/rabbit@node2.p
- 如何使用微博自动记录俯卧撑个数
- 在Ubuntu QML应用中实现Particle特效
- 添加/删除osd节点步骤
- 【BZOJ 1877】 [SDOI2009]晨跑
- apache2+passenger遇到的问题总结:
- CALayer Mask - 2 - CAShapeLayer
- 写给想成为Linux 高手的人们
- rewqrewqrqwe
- Unity错误集锦之插件篇
- 安卓学习日志(2)UDP通信
- datatables 使用心得
- android findViewById(...)的抽取
- 89785784567756
- 123512343124
- Code Project网站上关注的一些人