0-1背包問題入門


        最近在做背包問題,今天寫點東西總結一下。

        背包問題,常見的有三種類型:基本的0-1背包、完全背包和多重背包、二維背包 

        首先是基本的0-1背包問題。因為這里的物品一般指花瓶、玉器什么的,要么拿、要么不拿,只有0和1兩種狀態,所以也叫0-1背包。0-1背包雖然簡單,卻很重要,是“萬法之源”,是其他幾類問題的基礎。 

        初學者有時會認為,0-1背包可以這樣求解:計算每個物品的Vi/Wi,然后依據Vi/Wi的值,對所有的物品從大到小進行排序。其實這種貪心方法是錯誤的。如下表,有三件物品,背包的最大負重量是50,求可以取得的最大價值。 

        其實,0-1背包是DP的一個經典實例,可以用動態規划求解。

DP求解過程可以這樣理解:對於前i件物品,背包剩余容量為j時,所取得的最大價值(此時稱為狀態3)只依賴於兩個狀態。

狀態1:前i-1件物品,背包剩余容量為j。在該狀態下,只要不選第i個物品,就可以轉換到狀態3。

狀態2:前i-1件物品,背包剩余容量為j-w[i]。在該狀態下,選第i個物品,也可以轉換到狀態3。

因為,這里要求最大價值,所以只要從狀態1和狀態2中選擇最大價值較大的一個即可。 

狀態轉換方程:

dp( i,j ) = Max( dp( i-1, j ), dp( i-1, j-w[i] ) + v[i] )

 

dp( i,j )表示前i件物品,背包剩余容量為j時,所取得的最大價值。 

        還是結合上面的例子來說明吧。有三件物品,背包的最大負重量是50,求可以取得的最大價值。下圖表示了DP自上而下的求解過程。

編程實現:

       一般來說,有了狀態方程,直接編程實現就game over。dp( i,j ),用一個二維數組來實現,然后用一個兩層循環就可以了。不過,有時選擇的物品很多,背包的容量很大,這時要用二維數組往往是不現實的。這里有一個方法,可以進行空間壓縮,然后使用一維數組實現。

       還是結合上面的例子,有三件物品,背包的最大負重量是5,求可以取得的最大價值。為了方面說明,物品weight依次為:1,2,3。二維數組下的求解順序,物品數1--->n, 背包容量1--->w。如圖,要使用一維數組,背包容量要采用倒序,即w--->1, 只有這樣對於方程dp( j ) = Max( dp( j ), dp (j-w[i] ) + v[i] ),才能達到等式左邊才表示i,而等式右邊表示i-1的效果。POJ對於題目:3624。下面附代碼。

 

        完全背包和多重背包。有了基本的0-1背包基礎,下面的東西也就好理解了。 完全背包,指每個物品有無限多個。 多重背包,指每個物品的數量是有限的。當然,這時的問題不再是拿與不拿,而是拿多少的問題,當然不能超過背包容量。 

狀態轉換方程:

 

dp( i,j ) = Max( dp( i-1, j ), dp( i-1, j-k*w[i]) + k*v[i] ) ( 0 <= k <= c/ w[i] )

編碼實現:

如果直接編碼,用三層循環,往往會超時。這樣有一種很有效的壓縮方式:二進制壓縮。把原來的物品按照2的n次方進行重新組合。用1、2、4、8…進行組合,可以組合出任意的數字。POJ題目:1276


POJ3624

 

#include <iostream>
using namespace std;

//***********************常量定義*****************************

const int MAX_NUM = 3500;
const int MAX_WEIGHT = 14000;

//*********************自定義數據結構*************************



//********************題目描述中的變量************************

int weight[MAX_NUM];
int value[MAX_NUM];


//**********************算法中的變量**************************

//進行空間壓縮,使用一維數組
int dp[MAX_WEIGHT];


//***********************算法實現*****************************

void Solve( int n, int w )
{    
    for( int i=1; i<=n; i++ )
    {
        //因為使用了一維數組,所有j要按照遞減順序
        for( int j=w; j>=weight[i]; j-- )
        {            
            if( dp[j-weight[i]] + value[i] > dp[j] )
                dp[j] = dp[j-weight[i]] + value[i];            
        }
    }
    cout << dp[w] << endl;
}


//************************main函數****************************

int main()
{
    //freopen( "in.txt", "r", stdin );    

    int n, w;
    cin >> n >> w;        

    for( int i=1; i<=n; i++ )
    {
        cin >> weight[i] >> value[i];
    }
    Solve( n, w );
    
    return 0;
}

POJ1276
#include <iostream>
using namespace std;

//***********************常量定義*****************************

const int MAX_NUM = 1005;
const int MAX_CASH_REQUEST = 100005;


//*********************自定義數據結構*************************




//********************題目描述中的變量************************

int cashRequest;
int cashKind;


//**********************算法中的變量**************************
//dp[i][j]表示前i個物件,cashRequest == j時,所能獲得的最大金額
int dp[MAX_CASH_REQUEST];
//使用二進制壓縮,形成的新物件
int cnt;
int value[MAX_NUM];


//***********************算法實現*****************************

void Solve()
{
    //采用0-1背包求解
    for( int i=1; i<=cnt; i++ )
    {        
        for( int j=cashRequest; j>=value[i]; j-- )
        {
            dp[j] = dp[j] > dp[j-value[i]] + value[i] ? dp[j] : dp[j-value[i]] + value[i];
        }                
    }
    cout << dp[cashRequest] << endl;
}


//************************main函數****************************

int main()
{
    //freopen( "in.txt", "r", stdin );
    
    while( cin >> cashRequest >> cashKind )
    {
        //輸入
        for( int i=1; i<=cashKind; i++ )
        {
            int num, deno;
            cin >> num >> deno;
            
            //二進制壓縮
            for( int j=1; j<=num; j*=2 )
            {
                value[++cnt] = deno * j;
                num -= j;
            }                
            if( num > 0 )    value[++cnt] = num * deno;            
        }

        //處理
        Solve();
        
        //清空全局變量
        cnt = 0;
        memset( value, 0, sizeof(value) );
        memset( dp, 0, sizeof(dp) );
    }    

    return 0;
}

 

 
         

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM