2755:神奇的口袋,考點:動規、揹包問題簡單版
阿新 • • 發佈:2021-07-09
原題:http://bailian.openjudge.cn/practice/2755/
描述
有一個神奇的口袋,總的容積是40,用這個口袋可以變出一些物品,這些物品的總體積必須是40。John現在有n個想要得到的物品,每個物品的體積分別是a1,a2……an。John可以從這些物品中選擇一些,如果選出的物體的總體積是40,那麼利用這個神奇的口袋,John就可以得到這些物品。現在的問題是,John有多少種不同的選擇物品的方式。
輸入
輸入的第一行是正整數n (1 <= n <= 20),表示不同的物品的數目。接下來的n行,每行有一個1到40之間的正整數,分別給出a1,a2……an的值。
輸出
輸出不同的選擇物品的方式的數目。
樣例輸入
3 20 20 20
樣例輸出
3
解法
思路:動態規劃重要的題!!!
dp[i][j]:考慮前i種,湊出的體積為j時選擇物品的方式數目
邊界條件:
dp[i][0]=1;dp[0][a[0]]=1(在第一個東西能裝進去的前提下)
然後就可以從上往下進行計算了,
dp[i][j]先等於dp[i-1][j](不放進去),如果j可以讓它放進去,就加上放進去的種數dp[i-1][j-a[i]]
自己寫的程式碼如下:
#include <iostream> #include <cstring> #include <algorithm> using namespacestd; int dp[25][41];//dp[i][j]表示考慮前i種,可用體積為j的時候不同的選擇物品的方式數目 int a[25]; int main() { memset(dp, 0, sizeof(dp)); memset(a, 0, sizeof(a)); int n; cin >> n; for (int i = 0; i < n; i++) cin >> a[i]; for (int i = 0; i < n; i++) dp[i][0] = 1; if(a[0]<=40) dp[0][a[0]] = 1; for (int i = 1; i < n; i++) { for (int j = 1; j <= 40; j++) { dp[i][j] = dp[i - 1][j]; if (j >= a[i]) dp[i][j] += dp[i - 1][j - a[i]]; } } cout << dp[n - 1][40] << endl; return 0; }
種數、體積哪個做第一維變數都可以,如果是體積做第一維變數:
#include <iostream> using namespace std; int a[40]; int N; int Ways[50][40];//Ways[i][j]表示從前j種物品裡湊出體積i的方法數 int main() { cin >> N; memset(Ways, 0, sizeof(Ways)); for (int i = 1; i <= N; ++i) { cin >> a[i]; Ways[0][i] = 1; } Ways[0][0] = 1; for (int w = 1; w <= 40; ++w) { for (int k = 1; k <= N; ++k) { Ways[w][k] = Ways[w][k - 1]; if (w - a[k] >= 0) Ways[w][k] += Ways[w - a[k]][k - 1]; } } cout << Ways[40][N]; return 0; }
還可以用我為人人型動規節省空間。int sum,判斷sum[40]可達了幾次。
#include <iostream> using namespace std; #define MAX 41 int main() { int n, i, j, input; int sum[MAX]; for (i = 0; i < MAX; i++) sum[i] = 0; cin >> n; for (i = 0; i < n; i++) { cin >> input; for (j = 40; j >= 1; j--) if (sum[j] > 0 && j + input <= 40) sum[j + input] += sum[j]; // 如果j有sum[j]種方式可達,則每種方式加上input就可達 j + input sum[input]++; } cout << sum[40] << endl; return 0; }