01背包类型问题的两种解法
来源:互联网 发布:不上班的23种活法 知乎 编辑:程序博客网 时间:2024/06/05 06:08
这里讲两道题目(类型均是01背包类型的),两道题目均用了回溯法和动态规划两种解决办法,做了以后还是有所启发的。
第一道题目就是著名的01背包问题。
01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2……Wn,与之相对应的价值为P1,P2……Pn。求背包能放的最大价值。
回溯法的解法:
//0-1背包问题,假定n为8(总共有8种物品),M=110(总共能放的重量为110),重量是w={1,11,21,23,33,43,45,55},价值p={11,21,31,33,43,53,55,65},且已按单位价值排好序#define maxsize 9#include <IOSTREAM>using namespace std;int n=8;int M=110; //总共能放的重量为110int bestp=0; //最大的价值int noww=0; //当前容量double nowp=0; //当前价值,会出现小数的int x[maxsize]={0}; //用来判断物品是否被选int bestx[maxsize]={0}; //用来表示最优解下哪些物品被选int w[maxsize]={0,1,11,21,23,33,43,45,55}; //物品重量,0号元素不用int p[maxsize]={0,11,21,31,33,43,53,55,65}; //价值,0号元素不用int Check(int noww,int k,int nowp) { int flag=0; for (int i=k+1;i<=n;i++) { if (noww+w[i]<M) //如果可以放就放进去 { noww=noww+w[i]; nowp=nowp+p[i]; } else { flag=1; //flag=1表示当前有放不进去的 break; } } if (flag==1) //如果不是全部可以放进去 { nowp=nowp+(p[i]/w[i])*(M-noww); //分割后装满算最大价值 } return nowp;}void Backtrack(int noww,int k,int nowp) //noww为当前的重量,k为层数(第几个物品),nowp为现在的价值{ if (k>n) //表明所有的物品已经全部选择过了 { if (nowp>bestp||bestp==0) //1.当前的解比之前的最优解还要好,把当前解给最优解。2.如果是第一次也要赋值 { bestp=nowp; for(int i=1;i<=n;i++) { bestx[i]=x[i]; } } return; //要返回不然会一直递归下去 } if(noww+w[k]<=M) //如果到这一层为止的数小于M,则进入左子树,否则一定不可能,左子树便不生成 { x[k]=1; Backtrack(noww+w[k],k+1,nowp+p[k]); } if (Check(noww,k,nowp)>=bestp) //重要的剪枝,如果这一层不加,加上剩余的最大值(考虑分割,虽然不能取,但可以找到一个最大值),如果这个最大值比最优解还要小,那右子树不用考虑 { x[k]=0; Backtrack(noww,k+1,nowp); }}int main(){ cout<<"0-1背包问题,假定n为8(总共有8种物品),M=110(总共能放的重量为110),重量是w={1,11,21,23,33,43,45,55},价值p={11,21,31,33,43,53,55,65},且已按单位价值排好序。"<<endl<<endl<<endl; Backtrack(0,1,0); cout<<"最优解是:选重量是"; for (int i=1;i<=n;i++) { if (x[i]==1) { cout<<w[i]<<","; } } cout<<"总价值为"<<bestp<<"的物品。"<<endl; return 0;}
动态规划的解法:
//0-1背包问题#include <IOSTREAM>using namespace std;int n=4;int C=11;int V[4+1][11+1];int p[4+1]={0,3,4,5,7};int w[4+1]={0,2,3,5,6};int X[11+1];int Knapsack(){ for (int i = 0; i <= C; ++i) { X[i] = 0; } for (int j = 1; j <= n; ++j) { for (int i = C; i >= 1; --i) { if (i >= w[j] && X[i - w[j]] + p[j] > X[i]) { X[i] = X[i - w[j]] + p[j]; } } } return X[C];}int main(){ int bestp=Knapsack(); cout<<"选总价值为"<<bestp<<"的物品。"<<endl; return 0;}
第二道题目是去年暑假做的。浙大07年考研复试题最大报销额。
题目是这样的:
现有一笔经费可以报销一定额度的发票。允许报销的发票类型包括买图书(A类)、文具(B类)、差旅(C类),要求每张发票的总额不得超过1000元,每张发票上,单项物品的价值不得超过600元。现请你编写程序,在给出的一堆发票中找出可以报销的、不超过给定额度的最大报销额。
输入:
测试输入包含若干测试用例。每个测试用例的第1行包含两个正数 Q 和 N,其中 Q 是给定的报销额度,N(N<=30)是发票张数。随后是 N 行输入,每行的格式为:
m Type_1:price_1 Type_2:price_2 … Type_m:price_m
其中正整数 m 是这张发票上所开物品的件数,Type_i 和 price_i 是第 i 项物品的种类和价值。物品种类用一个大写英文字母表示。当N为0时,全部输入结束,相应的结果不要输出。
输出:
对每个测试用例输出1行,即可以报销的最大数额,精确到小数点后2位。
样例输入:
200.00 3
2 A:23.50 B:100.00
1 C:650.00
3 A:59.99 A:120.00 X:10.00
1200.00 2
2 B:600.00 A:400.00
1 C:200.50
1200.50 3
2 B:600.00 A:400.00
1 C:200.50
1 A:100.00
100.00 0
样例输出:
123.50
1000.00
1200.50
当时碰到这道题目,第一想法就是用回溯法,于是写下了如下程序:
#include <iostream>#include <vector>#include <string.h>#include <string>#include <stdlib.h>using namespace std;typedef struct Unit{ double A; double B; double C; double sum;}Unit;vector <Unit> ve;double maxsum, Q;void dfs(int k, double sum){ if (k >= ve.size()) { if (sum>maxsum) { maxsum = sum; } } else { dfs(k + 1, sum); if (sum + ve[k].sum <= Q) { dfs(k + 1, sum + ve[k].sum); } }}int main(){ int i, N, j, num, flag; double ctmp; string temp, subtmp; Unit utmp; while (cin >> Q >> N, N != 0) { ve.clear(); for (i = 0; i<N; i++) { flag = 0; utmp.A = 0; utmp.B = 0; utmp.C = 0; utmp.sum = 0; cin >> num; for (j = 0; j<num; j++) { cin >> temp; if (temp[0] == 'A') { subtmp = temp.substr(2); ctmp = atof(subtmp.data()); if (ctmp <= 600) { utmp.A += ctmp; utmp.sum += ctmp; } else { flag = 1; break; } } else if (temp[0] == 'B') { subtmp = temp.substr(2); ctmp = atof(subtmp.data()); if (ctmp <= 600) { utmp.B += ctmp; utmp.sum += ctmp; } else { flag = 1; break; } } else if (temp[0] == 'C') { subtmp = temp.substr(2); ctmp = atof(subtmp.data()); if (ctmp <= 600) { utmp.C += ctmp; utmp.sum += ctmp; } else { flag = 1; break; } } else { flag = 1; break; } } if (flag == 0 && utmp.sum <= 1000 && utmp.sum <= Q) { ve.push_back(utmp); } } maxsum = 0; dfs(0, 0); cout.setf(ios::fixed); cout.precision(2); cout << maxsum << endl; } system("pause"); return 0;}
然而提交的时候发现超时了,看来回溯法还是太慢了。后来在网上找答案时发现了这道题的动态规划解法,由于是别人的代码,我这边就不贴了。网址是http://blog.csdn.net/xiexingshishu/article/details/8799669。
由于当时年轻(其实是笨),没有发现这其实就是个01背包问题,若回溯法时间太长的话可以用动态规划解来减少时间。
- 01背包类型问题的两种解法
- Exercise(4):01背包(简单动规)问题的两种解法
- 01背包问题的三种解法
- 0-1背包问题的两种解法(回溯法和动态规划)
- 01 背包问题的另类解法
- 01背包问题的优化解法
- 01背包问题的动态规划解法
- 背包问题的几种解法总结
- 01背包的两种初始化问题
- POJ 1742 Coins (多重背包的两种解法)
- 八皇后问题的两种解法
- LCA问题的两种解法
- 汉诺塔问题的两种解法
- 八皇后问题的两种解法
- 8皇后问题的两种解法
- 八皇后问题的两种解法
- 背包分组问题的解法
- 背包问题的解法源代码
- android之Button2
- linux下vi命令大全
- 关于Integer的parseInt(String s, int radix)方法的使用
- Robert 的军队
- 如何选择 compileSdkVersion, minSdkVersion 和 targetSdkVersion
- 01背包类型问题的两种解法
- SQL语句
- 《庄子》的逍遥
- eclipse安装maven
- Linux开发工具之调试器gdb
- mysql主主复制+Keepalived 打造高可用mysql集群
- 开发-开发中遇到三目运算符的问题
- C#的语法
- Android基本的四大组件介绍