题意:给你n种物品,有m块钱,然后给出n种物品的花费,问物品个数最多时的组合种数有多少。
思路:背包九讲上提过如何求解方案数的方法,一开始还是没怎么懂。
dp[i][j]表示使用i元钱购买j件物品的方案数。
那么由01背包可以知道对应一种物品,要么选,要么不选,再加上这里用的是滚动数组(实际上是三维dp,这里优化到了二维),所以对于前i件物品花费j元选用k件物品的方案数=dp[j][k] (表示不选第i件物品)+dp[j-a[i]][k-1] (表示选用了第i件物品)
所以递推公式是 :dp[j][k] = dp[j][k] + dp[j - a[i]][k - 1];
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 35;
int t, n, m, a[maxn];
int dp[505][maxn];//dp[i][j]i元钱购买j个物品的方案数
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
sort(a + 1, a + 1 + n);
int ans = 0;
int sum = 0;
for(int i = 1; i <= n; i++) {
if(sum + a[i] <= m) {
sum += a[i];
ans++;
}
}
if(ans == 0) {
printf("Sorry, you can't buy anything.\n");
} else {
memset(dp, 0, sizeof(dp));
for(int i = 0; i <= m; i++) dp[i][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = m; j >= a[i]; j--) {
for(int k = ans; k>=1; k--) {//跟这个顺序无关,可以顺也可以逆
dp[j][k] = dp[j][k] + dp[j - a[i]][k - 1];
}
}
}
printf("You have %d selection(s) to buy with %d kind(s) of souvenirs.\n", dp[m][ans], ans);
}
}
return 0;
}