0-1 背包問題詳解

給定一組有固定價值和固定重量的物品,以及一個已知最大承重量的背包,求在不超過背包最大承重量的前提下,能放進背包里面的物品的最大總價值。

這一類問題是典型的使用動態(tài)規(guī)劃解決的問題,我們可以把背包問題分成3種不同的子問題:0-1背包問題、完全背包和多重背包問題。下面對這三種問題分別進行討論。

1.0-1背包問題

0-1背包問題是指每一種物品都只有一件,可以選擇放或者不放?,F(xiàn)在假設有n件物品,背包承重為m。

對于這種問題,我們可以采用一個二維數(shù)組去解決:f[i][j],其中i代表加入背包的是前i件物品,j表示背包的承重,f[i][j]表示當前狀態(tài)下能放進背包里面的物品的最大總價值。那么,f[n][m]就是我們的最終結果了。

采用動態(tài)規(guī)劃,必須要知道初始狀態(tài)和狀態(tài)轉移方程。初始狀態(tài)很容易就能知道,那么狀態(tài)轉移方程如何求呢?對于一件物品,我們有放進或者不放進背包兩種選擇:

(1)假如我們放進背包,f[i][j] = f[i - 1][j - weight[i]] + value[i],這里的f[i - 1][j - weight[i]] + value[i]應該這么理解:在沒放這件物品之前的狀態(tài)值加上要放進去這件物品的價值。而對于f[i - 1][j - weight[i]]這部分,i - 1很容易理解,關鍵是 j - weight[i]這里,我們要明白:要把這件物品放進背包,就得在背包里面預留這一部分空間。
(2)假如我們不放進背包,f[i][j] = f[i - 1][j],這個很容易理解。因此,我們的狀態(tài)轉移方程就是:f[i][j] = max(f[i][j] = f[i - 1][j] , f[i - 1][j - weight[i]] + value[i]) 。當然,還有一種特殊的情況,就是背包放不下當前這一件物品,這種情況下f[i][j] = f[i - 1][j]。


#include <iostream>
#define V 500
using namespace std;
int weight[20 + 1];
int value[20 + 1];
int f[V + 1];
int main() {
    int n, m;
    cout << "請輸入物品個數(shù):";
    cin >> n;
    cout << "請分別輸入" << n << "個物品的重量和價值:" << endl; 
    for (int i = 1; i <= n; i++) {
        cin >> weight[i] >> value[i];
    }
    cout << "請輸入背包容量:";
    cin >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = m; j >= 1; j--) {
            if (weight[i] <= j) {
                f[j] = f[j] > f[j - weight[i]] + value[i] ? f[j] : f[j - weight[i]] + value[i];
            }
        }
    }
    cout << "背包能放的最大價值為:" << f[m] << endl;
}

題目描述

這個表格對于理解0-1背包問題很有用,我們利用它來理解一下為什么要把第二層循環(huán)顛倒這個問題??紤]d9這一項,要求出這個狀態(tài),我們有可能利用到的就是e1到e8這8個狀態(tài),當我們把第二層循環(huán)顛倒過來時,當我們要求f[j]時,f[j -1]到f[1]還保存著下面一行的狀態(tài),因此可以采用一維數(shù)組解決。我們可以考慮一下假如不把第二層循環(huán)顛倒,當要求f[j]時,f[j - 1]到f[1]已經(jīng)是同一行的狀態(tài)了,根本沒法求。

for (int i = 1; i <= n; i++) {
     for (int j = m; j >= 1; j--) {
         if (weight[i] <= j) {
             f[j] = f[j] > f[j - weight[i]] + value[i] ? f[j] : f[j - weight[i]] + value[i];
         }
     }
}

2.完全背包問題

#include <iostream>
#define V 500
using namespace std;
int weight[20 + 1];
int value[20 + 1];
int f[V + 1];
int max(int a, int b) {
    return a > b ? a : b;
}
int main() {
    int n, m;
    cout << "請輸入物品個數(shù):";
    cin >> n;
    cout << "請分別輸入" << n << "個物品的重量和價值:" << endl; 
    for (int i = 1; i <= n; i++) {
        cin >> weight[i] >> value[i];
    }
    cout << "請輸入背包容量:";
    cin >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = weight[i]; j <= m; j++) {
            f[j] = max(f[j], f[j - weight[i]] + value[i]);
        }
    }
    cout << "背包能放的最大價值為:" << f[m] << endl;
}

完全背包問題與0-1背包問題的區(qū)別
1.完全背包問題內(nèi)層循環(huán)是順序的,而0-1背包問題是逆序的。。
0-1背包為逆序的原因是為了求max中的兩項是前一狀態(tài)的值。
而現(xiàn)在是要求當前狀態(tài)的值,因為每種背包都是無限的,當我們從1到N循環(huán)時,f[v]表示容量為v在前i種背包所得的價值,這里我們添加的不是前一個背包,而且當前背包,所以應考慮當前狀態(tài)。

3.多重背包問題

多重背包問題限定了一種物品的個數(shù),解決多重背包問題,只需要把它轉化為0-1背包問題即可。比如,有2件價值為5,重量為2的同一物品,我們就可以分為物品a和物品b,a和b的價值都為5,重量都為2,但我們把它們視作不同的物品。

#include <iostream>
using namespace std;
#define V 1000
int weight[50 + 1];
int value[50 + 1];
int num[20 + 1];
int f[V + 1];
int max(int a, int b) {
    return a > b ? a : b;
}
int main() {
    int n, m;
    cout << "請輸入物品個數(shù):";
    cin >> n;
    cout << "請分別輸入" << n << "個物品的重量、價值和數(shù)量:" << endl; 
    for (int i = 1; i <= n; i++) {
        cin >> weight[i] >> value[i] >> num[i];
    }
    int k = n + 1;
    for (int i = 1; i <= n; i++) {
        while (num[i] != 1) {
            weight[k] = weight[i];
            value[k] = value[i];
            k++;
            num[i]--;
        }
    }
    cout << "請輸入背包容量:";
    cin >> m;
    for (int i = 1; i <= k; i++) {
        for (int j = m; j >= 1; j--) {
            if (weight[i] <= j) f[j] = max(f[j], f[j - weight[i]] + value[i]);
        }
    }
    cout << "背包能放的最大價值為:" << f[m] << endl;
}
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 網(wǎng)上好多關于背包問題的解釋,自己也看了,感覺解釋的不容易通俗易懂,所以自己來寫一個非常容易懂得。 0-1背包問題說...
    sydt2011閱讀 37,013評論 3 17
  • 我在進行一些互聯(lián)網(wǎng)公司的技術筆試的時候,對于我來說最大的難題莫過于最后的那幾道編程題了,這對算法和數(shù)據(jù)結構有一定程...
    檸檬烏冬面閱讀 20,317評論 2 19
  • 感冒繼續(xù),去藥店買藥,需要登記身份證,說了號碼也就賣了。都說感冒吃不吃藥都要七天,不過吃藥可以緩解一下癥狀,身體不...
    董克平日記閱讀 588評論 0 0
  • 一圈圈, 一層層, 我在外面,你在里面。 你說, 天氣很好, 我說, 這里暴雨連連, 你懷疑我, 我懷疑你。
    小娘紙閱讀 381評論 0 3
  • 一、版本控制 用文檔記錄每個模塊的改動,存儲、追蹤修改歷史。方便恢復版本以及版本查看。 二、版本控制軟件 - SV...
    學飛的小雞閱讀 326評論 0 0

友情鏈接更多精彩內(nèi)容