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;
}
如有錯誤,煩請指正😀