今天在看july的博客之时,看到其中一道题目的原理为01背包问题,就自己温习了下,写下今天的学习体会。
本文理论分析参考博客:http://www.cnblogs.com/qinyg/archive/2012/04/26/2471829.html
问题描述:
给定N个物品和一个背包,其中物品i的重量是Wi,其价值为Vi ,背包的容量为C。问应该如何选择装入背包的物品,使得转入背包的物品的总价值为最大?
(关于01背包问题的其他变种请参看
http://zh.wikipedia.org/wiki/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98
http://love-oriented.com/pack/
)
对于此类01背包问题,我们可以使用动态规划分析解决:
问题分析:
在选择物品的时候,对每种物品i只有两种选择,即装入背包或不装入背包。不能讲物品i装入多次,也不能只装入物品的一部分。因此,该问题被称为0-1背包问题。
令V(i,j)表示在前i(1<=i<=n)个物品中能够装入容量为就j(1<=j<=C)的背包中的物品的最大价值,则可以得到如下的动态规划函数:
(1) V(i,0)=V(0,j)=0
(2) V(i,j)=V(i-1,j) j<wi
(3) V(i,j)=max{V(i-1,j) ,V(i-1,j-wi)+vi) } j>wi
(2)表明:如果第i个物品的重量大于背包的容量,则装人前i个物品得到的最大价值和装入前i-1个物品得到的最大价是相同的,即物品i不能装入背包;
(3)表明:如果第i个物品的重量小于背包的容量,则会有一下两种情况:(a)如果把第i个物品装入背包,则背包物品的价值等于第i-1个物品装入容量位j-wi 的背包中的价值加上第i个物品的价值vi;(b)如果第i个物品没有装入背包,则背包中物品价值就等于把前i-1个物品装入容量为j的背包中所取得的价值。显然,取二者中价值最大的作为把前i个物品装入容量为j的背包中的最优解。
(注:一开始大家对第三个式子比较难以理解,我们可以从这么几点帮助理解:
1.这前i个中,并不是所有的i都被选择了,请大家看下面的代码会更加加深理解。
2.动态规划的概念,有最有子结构,重复子问题。V(i,j)其实是有记录作用的。
)
我们来看下代码实现:
#include<iostream> #include<cstdlib> using namespace std; int maxNum(int a,int b) { return (a>b)?a:b; } void insight(int *exist,int **val,int *w,int bagnum,int weight) { for(int i=bagnum;i>0;i--) { if(val[i][weight]>val[i-1][weight]) { exist[i]=1; weight=weight-w[i]; } } for(int j=1;j<=bagnum;j++) cout<<exist[j]<<" "; } int maxVal(int **val,int *v,int *w,int bagnum,int weight) { int i,j; for(i=0;i<=bagnum;i++) val[i][0]=0; for(i=0;i<=weight;i++) val[0][i]=0; for(i=1;i<=bagnum;i++) { for(j=1;j<=weight;j++) { if(j<w[i]) val[i][j]=val[i-1][j]; else val[i][j]=max(val[i-1][j],val[i-1][j-w[i]]+v[i]); } } for(i=0;i<=bagnum;i++) for(j=0;j<=weight;j++) { cout<<val[i][j]<<" "; if(j==weight) cout<<endl; } return val[bagnum][weight]; } int main() { int bagnum,weight; int i,j; cout<<"please input the number of bags:"<<endl; cin>>bagnum; cout<<"please input the limit weight of bag:"<<endl; cin>>weight; int **val=new int *[bagnum+1]; //这样定义二维数组,是为了方便传入 val[0]=new int [(bagnum+1)*(weight+1)]; for(i=1;i<=bagnum;i++) val[i]=val[i-1]+weight+1; cout<<"please input the weight of every bag:"<<endl; int *w=new int[bagnum+1](); for(i=1;i<=bagnum;i++) cin>>w[i]; //cout<<"w="; //for(i=0;i<=bagnum;i++) // cout<<w[i]<<" "; //cout<<endl; cout<<"please input the value of every bag:"<<endl; int *v=new int[bagnum+1](); for(i=1;i<=bagnum;i++) cin>>v[i]; //cout<<"v="; //for(i=0;i<=bagnum;i++) // cout<<v[i]<<" "; //cout<<endl; cout<<maxVal(val,v,w,bagnum,weight)<<endl; int *exist=new int[bagnum](); //为了查看我们选择了哪些包 cout<<"we get the bag num is:"; insight(exist,val,w,bagnum,weight); cout<<endl; // int **val=(int **)malloc((bagnum+1)*sizeof(int *)); // for(i=0;i<bagnum+1;i++) // val[i]=(int *)malloc((weight+1)*sizeof(int)); // return 0; }
看完此处之后,我们在来看下july大神写的关于
请看http://blog.csdn.net/v_july_v/article/details/6419466
第二节、寻找和为定值的多个数
第21题(数组)
2010年中兴面试题
编程求解:
输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,
使其和等于 m ,要求将其中所有的可能组合列出来
并附上我的代码,较原文给出的会更加容易理解一点
#include<iostream> #include<list> using namespace std; list<int> lis; //这边我们将list用作了栈,当然也可以用stack容器,不过stack其实是一种容器适配器,其默认实现方式是用list实现的 void findNSum(int sum,int n) { if(sum<=0||n<=0) //递归退出条件 return; if(sum==n) //打印条件 { for(list<int>::iterator iter=lis.begin();iter!=lis.end();iter++) cout<<*iter<<"+"; cout<<n<<endl; //这是由于,此时的n并没有被压在原队列中 } lis.push_back(n); //将n压入list中 findNSum(sum-n,n-1); //n装入以后,刚好等于sum lis.pop_back(); //将刚才压入list中的数据弹出 findNSum(sum,n-1); //没有选择n,说明前面n-1个数已经就可以满足条件 } int main() { int sum,n; cout<<"please input the sum:"<<endl; cin>>sum; cout<<"please input the n:"<<endl; cin>>n; findNSum(sum,n); return 0; }