01揹包問題 及c++ 程式碼實現
今天在看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
(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;
}