01揹包與最小郵票數

01揹包與最小郵票數

牛客地址

最小郵票數,這是一道典型的01揹包變形問題

01揹包

輸入描述

有多組輸入數據

輸入揹包大小m,物品件數n

接下來n行依次輸入,第i件物品的重量和價值

輸入樣例

90 4

20 25

30 20

40 50

10 18

40 2

25 30

10 8

輸出描述

輸出能放入揹包的最大價值

輸出

95

38

典型的01揹包狀態轉移方程如下

dp[i][j]表示前i個物品裝進容量爲j的揹包能獲得的最大價值
dp[i][j] = max{dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i] | j >= weight[i]}
dp[i][0] = 0 (0 <= i <= n)
dp[0][j] = 0 (0 <= j <= m)

板子如下

int main() {
    int m, n;
    while (cin >> m >> n) {
        int dp[m][n];
        int weight[n + 1];
        int value[n + 1];
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= m; ++i) {
            scanf("%d%d", &weight[i], &value[i]);
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = m; j >= weight[i]; --j) {
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
            }
        }
        cout << dp[m][n] << endl;
    }
    return 0;
}

最小郵票數

有若干張郵票,要求從中選取最少的郵票張數湊成一個給定的總值。 如,有1分,3分,3分,3分,4分五張郵票,要求湊成10分,則使用3張郵票:3分、3分、4分即可。

輸入描述:

有多組數據,對於每組數據,首先是要求湊成的郵票總值M,M<100。然後是一個數N,N〈20,表示有N張郵票。接下來是N個正整數,分別表示這N張郵票的面值,且以升序排列。

輸入

10
5
1 3 3 3 4

輸出描述:

對於每組數據,能夠湊成總值M的最少郵票張數。若無解,輸出0。

輸出

3

與01揹包問題不同的是,

  • 這道題沒有郵票價值的數組
  • 這道題需要所需郵票數最小值

那麼如何構造狀態轉移公式呢?

這時,我們還是首先定於dp數組


同01揹包一樣的思想,定義dp[i][j]表示前i個郵票,集齊j所需要的郵票數(不再是01揹包的總價值

  • 若第i個郵票不放入,則相當於前i-1張郵票要集齊j, 則dp[i][j] = dp[i - 1][j]
  • 若第j個郵票放入,則相當於前i-1張郵票,集齊j-stamp[i],然後票數加一, 則dp[i][j] = dp[i - 1][j - stamp[i]] + 1(dp數組每個值表示郵票數,則這裏即爲+1操作,同時兩者取小,符合題目要求最小郵票數)

兩者取最小值:
dp[i][j] = min{dp[i - 1][j], dp[i - 1][j - stamp[i]] + 1 | j > stamp[i]}
dp[n][m]即爲所求。


dp數組定義完後,還有一個關鍵的問題,如何初始化dp數組,這個直接關係到dp數組能不能正確反映題目描述情況。

根據dp數組意義

  • dp[0...n][0]時,不管有多少張郵票,都要湊成0,則顯而易見,一張郵票都不用就行,所以dp[0...n][0] = 0
  • dp[0][1...m]時,沒有一張郵票給你,但是要湊大於0的數,這是不可能的,所以dp[0][1...m] = INF (一個很大的數,別是INT_MAX,會爆int)

到此,DP定義完畢,下來就是套01揹包的板子,再注意一下數組的邊界值的細節就行了

#include <bits/stdc++.h>
 
using namespace std;
 
int main() {
    int m, n;  // 集齊m,  n張郵票
    const int INF = 1000;
    while (cin >> m >> n) {
        int stamp[n + 1];
        int dp[n + 1][m + 1]; 
        // 初始化DOP
        for (int i = 0; i <= n; ++i) {
            for (int j = 0; j <= m; ++j) {
                if (j == 0) dp[i][j] = 0;
                else dp[i][j] = INF;
            }
        }
        // 輸入
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &stamp[i]);
        }
        // 構造DP
        for (int i = 1; i <= n; ++i) {
            for (int j = m; j >= stamp[i]; --j) {
                    dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - stamp[i]] + 1);
            }
        }
        int res = (dp[n][m] == INF) ? 0 : dp[n][m];
        cout << res << endl;
    }
    return 0;
}

如有錯誤,煩請指正😀

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章