DP-2

戴瑢溱

Index

簡介

給定一組物品,每個物品都有自己的「重量」與「價值」,在限定的總重量(W)中,選擇哪些物品裝入背包,能使背包內的總價值(V)最大化?

不超重的情況下 (總重量 <= W) 最大的總價值 !

限重:W

重量:W1

價值:V1

重量:W2

價值:V2

重量:W3

價值:V3

給定一組物品,每個物品都有自己的「重量」與「價值」,在限定的總重量(W)中,選擇哪些物品裝入背包,能使背包內的總價值(V)最大化?

限重:W

0/1背包問題

限重:W

重量:W1

價值:V1

重量:W2

價值:V2

重量:W3

價值:V3

物品只能選「拿(1)」或「不拿(0)」,且一種物品只有一個

限重:5

重量:1

價值:100

重量:3

價值:200

重量:4

價值:300

物品\重量     0     1     2     3     4     5




      (1, 100)        (3, 200)        (4, 300)

物品\重量     0     1     2     3     4     5
000000

      (1, 100)        (3, 200)        (4, 300)

不放任何東西,總價值皆為0

限重為0時,總價值皆為0

物品\重量     0     1     2     3     4     5
000000
0
0
0

      (1, 100)        (3, 200)        (4, 300)

放入一個手套,總價值為100

物品\重量     0     1     2     3     4     5
000000
0100
0
0

      (1, 100)        (3, 200)        (4, 300)

較大的背包一樣能放入手套,總價值皆為100

物品\重量     0     1     2     3     4     5
000000
0100100100100100
0
0

      (1, 100)        (3, 200)        (4, 300)

限重 < 3的背包只能放手套不能放手電筒,總價值為100

物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100
0

      (1, 100)        (3, 200)        (4, 300)

限重為3的背包可以放手電筒,價值為200

物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200
0

      (1, 100)        (3, 200)        (4, 300)

一個手套 + 一個手電筒,價值為300

物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0

      (1, 100)        (3, 200)        (4, 300)

以此類推…

物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0100100200300

      (1, 100)        (3, 200)        (4, 300)

一個手套 + 一個玩具飛機,總價值400

物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0100100200300400

      (1, 100)        (3, 200)        (4, 300)

We did it !

物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0100100200300400

      (1, 100)        (3, 200)        (4, 300)

這些數字彼此間有什麼關係呢?

物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0100100200300400

      (1, 100)        (3, 200)        (4, 300)

演算法在尋找最佳解的時候比較兩個選項

物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0100100200300400

      (1, 100)        (3, 200)        (4, 300)

A. 新物品的價值,加上剩餘空間能換到的最大價值

B. 不拿新物品,沿用不放這個物品時的最佳解

演算法在尋找最佳解的時候比較兩個選項

A. 新物品的價值,加上剩餘空間能換到的最大價值

B. 不拿新物品,沿用不放這個物品時的最佳解

物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0100100200300400

      (1, 100)        (3, 200)        (4, 300)

100 + 0

0

演算法在尋找最佳解的時候比較兩個選項

A. 新物品的價值,加上剩餘空間能換到的最大價值

B. 不拿新物品,沿用不放這個物品時的最佳解

200 + 100

100

物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0100100200300400

      (1, 100)        (3, 200)        (4, 300)

演算法在尋找最佳解的時候比較兩個選項

物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0100100200300400

      (1, 100)        (3, 200)        (4, 300)

A. 新物品的價值,加上剩餘空間能換到的最大價值

B. 不拿新物品,沿用不放這個物品時的最佳解

300 + 100

300

dp[i+1][j]=max(dp[i][j], dp[i][j-w[i]]+v[i]); 
物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0100100200300400

      (1, 100)        (3, 200)        (4, 300)

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false); // 加速輸入輸出
    cin.tie(0);

    int m, n;
    while (cin >> m >> n) {
        vector<int> w(m), v(m);
        for (int i = 0; i < m; i++) {
            cin >> w[i] >> v[i];
        }

        // 宣告 vector 二維 dp 表格, 初始化為 0
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

        for (int i = 0; i < m; i++) {
            for (int j = 0; j <= n; j++) {
                if (w[i] <= j) {
                    // dp[i+1] 代表當前物品,dp[i] 代表前一個狀態
                    dp[i + 1][j]
                    = max(dp[i][j], dp[i][j - w[i]] + v[i]);
                } else {
                    dp[i + 1][j] = dp[i][j];
                }
            }
        }
        cout << dp[m][n] << endl;
    }
    return 0;
}

但是二維陣列好大一個,

我的背包很大很佔空間欸 !

搗蛋鬼,別搗蛋 !

害我又要多學一個,

好吧我們來試試看一維陣列 !

dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0100100200300400

      (1, 100)        (3, 200)        (4, 300)

for (int j = n; j >= w[i]; j--)
物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0100100200300400

      (1, 100)        (3, 200)        (4, 300)

由後往前更新,能確保當前計算 dp[j] 時,所參考的 dp[j - w[i]] 依然是上一輪留下的結果,等同於二維表格中的「上一列」

 

for (int j = n; j >= w[i]; j--)
物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0100100200300400

      (1, 100)        (3, 200)        (4, 300)

如果由前往後更新,後面的重量會參考到「這輪已經放過物品」的狀態,導致同一個物品被重複放入(變成完全背包問題)

 

#include <bits/stdc++.h>
using namespace std;
int main() {
    int m, n;
    while (cin >> m >> n) {
        int w[m], v[m];
        for (int i = 0; i < m; i++)
            cin >> w[i] >> v[i];

        // 空間優化:只需長度為 n+1 的一維陣列
        vector<int> dp(n + 1, 0);

        for (int i = 0; i < m; i++) {
            // 由後往前遍歷
            for (int j = n; j >= w[i]; j--) {
                // dp[j] = max(原本不放的值, 放了新物品後的價值)
                dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
            }
            // 提示:當 j < w[i] 時,dp[j] 會維持原值,不需額外處理
        }
        cout << dp[n] << endl;
    }
    return 0;
}

完全背包問題

限重:W

重量:W1

價值:V1

重量:W2

價值:V2

重量:W3

價值:V3

每一樣物品都有好幾個,可以重複放!
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
物品\重量     0     1     2     3     4     5
000000
0100100100100100
0100100200300300
0100100200300400

      (1, 100)        (3, 200)        (4, 300)

#include <bits/stdc++.h>
using namespace std;
int main() {
    int m, n;
    while (cin >> m >> n) {
        int w[m], v[m];
        for (int i = 0; i < m; i++)
            cin >> w[i] >> v[i];

        vector<int> dp(n + 1, 0);

        for (int i = 0; i < m; i++) {
            // 正序遍歷,從重量 w[i] 往上加到 n
            for (int j = w[i]; j <= n; j++) {
                // dp[j-w[i]] 可能是已經放過第 i 種物品後的最佳值
                dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
            }
        }
        cout << dp[n] << endl;
    }
}

kahoot !

DP-2

By ariel tai

DP-2

  • 55