1. 程式人生 > 實用技巧 >完全揹包

完全揹包

問題

  有N種物品和一個容量是V的揹包,每種物品都有無限件可用。

  第i種物品的體積是vi,價值是wi

  求解將哪些物品裝入揹包,可使這些物品的總體積不超過揹包容量,且總價值最大。
  輸出最大價值。

  輸入格式

  第一行兩個整數,N,V,用空格隔開,分別表示物品種數和揹包容積。

  接下來有N行,每行兩個整數vi,wi,用空格隔開,分別表示第i種物品的體積和價值。

  輸出格式

  輸出一個整數,表示最大價值。

  資料範圍

  0<N,V1000
  0<vi,wi1000

定義f(i, j)表示前i個物品任意選擇不限次數總體積不大於j情況下的最大總價值,遞迴關係終點f(i, 0) = 0;

針對第i個物品,可以選擇k次,那麼狀態方程為:

f(i, j) = max { f(i - 1, j - k * vi) + k * wi } k>=0 1⃣️

= max ( f(i - 1, j), max { f(i - 1, j - k * vi) + k * wi } ) k>=1 2⃣️.可以考慮如果第i件物品一次不選的情況,就等於前i-1件物品的選擇

= max ( f(i - 1, j), max { f(i - 1, j - vi - k * vi) + k * wi + wi } ) k>=0 3⃣️.令k = k + 1

= max ( f(i - 1, j), f(i, j - vi) + wi ) 4⃣️.根據1⃣️公式推導過來,將j - vi 看作一個整體

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

const int N = 1001;

int n,m;

int p[N][N];

int v[N], w[N];

int main() {
    
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++) {
        cin >> v[i] >> w[i];
    }
    
    for (int i = 1; i <= n; i ++) {
        for (int j = 0; j <= m; j++) {
            p[i][j] = p[i - 1][j];
            if (j >= v[i]) {
                p[i][j] = max(p[i][j], p[i][j - v[i]] + w[i]);
            }
        }
    }
    
    cout << p[n][m] << endl;
    return 0;
}

同01揹包一樣,我們可以將二維陣列優化為一維資料。

f(i, j) = max ( f(i - 1, j), f(i, j - vi) + wi ) => f(j) = max ( f(j), f(j - vi) + wi )

此時我們需要保證max中 f(j)是上一個物品選擇的結果,f(j - vi)必須是當前物品已經計算過的選擇結果,那麼內層迴圈j就必須是從小到大來迴圈。

例如:第一個物品體積1,價值2,揹包總容量5;選擇第一個商品時,如果從大到小迴圈計算p[5] = max (p[5], p[3] + 2) = 2,此時p[3]=0是沒有選擇物品時的值;

但是實際上揹包容量是5的時候,我們可以將第一個物品放入兩次,所以計算p[5]時,一定要保證p[3]這個一定計算過放入第一個物品的結果。

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

const int N = 1001;

int n,m;

int p[N];

int v[N], w[N];

int main() {
    
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++) {
        cin >> v[i] >> w[i];
    }
    
    for (int i = 1; i <= n; i ++) {
        for (int j = v[i]; j <= m; j++) {
            p[j] = max(p[j], p[j - v[i]] + w[i]);
        }
    }
    
    cout << p[m] << endl;
    return 0;
}

  

如果要恰好裝滿揹包的話,同01揹包一樣,我們只要定義p[j] = -1,p[0] = 0即可。

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

const int N = 1001;

int n,m;

int p[N];

int v[N], w[N];

int main() {
    
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++) {
        cin >> v[i] >> w[i];
        p[i] = -1;
    }
    
    for (int i = 1; i <= n; i ++) {
        for (int j = v[i]; j <= m; j++) {
            if (p[j - v[i]] != -1) {
                p[j] = max(p[j], p[j - v[i]] + w[i]);
            }
        }
    }
    
    if (p[m] != -1) {
        cout << p[m] << endl;
    } else {
        cout << "無法裝滿揹包" << endl;
    }
    return 0;
}