背包问题求方案数

Problem

题目链接

Solution

此题在01背包的基础上,需要你求出最优选法的方案数

需要另开一个数组来记录方案数。
此处需要注意,虽然是01背包的另一种问法,但此题需要把dp[1…m]初始化为负无穷。这样做的原因是,如果dp数组全部初始化为0,那么dp[k]的含义为容量为k时背包装的最大价值(注意此处不一定被装满了),那么这样就会造成重复计数的问题。若把dp[1…m]初始化为负无穷,那么最大价值一定是从0开始转移的,也就是说若dp[k]是最大价值,那么一定装满了容量k。

#include <cstdio>
#include <climits>

const int N = 1e3 + 5;
const int mod = 1e9 + 7;
const int INF = INT_MIN;

int dp[N];
int g[N];

int main() {
    int n, m;
    scanf("%d %d", &n, &m);

    //若dp[1-m]=0 则dp的含义为容量为k时能装的最大价值 不一定用完k容量
    //因此把dp[1-m]=inf 这样处理后 dp[k]的含义为容量恰好为k时能装的最大价值
    for (int i = 1; i <= m; ++i) dp[i] = INF;

    g[0] = 1;

    for (int i = 1; i <= n; ++i) {
        int v, w;
        scanf("%d %d", &v, &w);
        for (int j = m; j >= v; --j) {
            int t = dp[j] > (dp[j - v] + w) ? dp[j] : (dp[j - v] + w);
            int s = 0;
            if (dp[j] == t) s += g[j];
            if (dp[j - v] + w == t)s += g[j - v];
            s %= mod;
            dp[j] = t;
            g[j] = s;
        }
    }

    int ma = 0;
    for (int i = 0; i <= m; ++i) ma = ma > dp[i] ? ma : dp[i];
    int ans = 0;
    for (int i = 0; i <= m; ++i) {
        if (ma == dp[i]) {
            ans += g[i];
            ans %= mod;
        }
    }
    printf("%d\n", ans);
    return 0;
}

若不改变dp数组的初始化方法,也可以改变方案计数数组g的初始化方法。
dp[k]与g[k]互相对应
dp[k]的初始含义是用前0件物品,去装容量为k的背包,能获得的最大价值。那么肯定有一种装法就是一件物品都不装,价值为0,因此dp数组全部初始化为0,g数组全部初始化为1。再去做01背包即可。

#include <cstdio>

const int N = 1e3 + 5;
const int mod = 1e9 + 7;

int dp[N];
int g[N];

int main() {
    int n, m;
    scanf("%d %d", &n, &m);

    for(int i=0;i<=m;++i) g[i]=1;
    
    for (int i = 1; i <= n; ++i) {
        int v, w;
        scanf("%d %d", &v, &w);
        for (int j = m; j >= v; --j) {
            if(dp[j]<dp[j-v]+w)
            {
                g[j]=g[j-v];
                dp[j]=dp[j-v]+w;
            }
            else if(dp[j]==dp[j-v]+w)
            {
                g[j]=(g[j]+g[j-v])%mod;
            }
        }
    }
    printf("%d\n", g[m]);
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值