hdu 2126 Buy the souvenirs(01背包求最优解的数量)

题意:给你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;
}

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/80460981