1. 程式人生 > >01揹包問題 及c++ 程式碼實現

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

)+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;
}