题目传送门

一、分析过程

本题是 背包DP 中的经典题型 —— 【背包DP求最优方案总数

\(01\)背包的转移方程\(f[i,j] = max(f[i - 1,j],f[i - 1,j - v] + w)\)

其中\(g[i,j]\)\(f[i,j]\)取最大值的方案数

\(f[i,j]\)是从\(f[i - 1,j]\)转移过来的,则\(g[i,j] = g[i - 1,j]\)

\(f[i,j]\)是从\(f[i - 1,j - v] + w\)转移过来的,则\(g[i,j] = g[i - 1,j - v]\)

\(f[i,j]\)均能从\(f[i - 1,j]\)\(f[i - 1,j - v] + w\)转移过来的,则\(g[i,j] = g[i - 1,j] + g[i - 1,j - v]\)

将所有的最大值所对应的方案数累加在一起,即为方案数总和

二、二维实现

#include <bits/stdc++.h>

using namespace std;
const int N = 1010;
const int MOD = 1e9 + 7;

int n, m;
int w[N], v[N];
int f[N][N], g[N][N];

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    //01背包求最大价值
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= m; j++) {//二维是正序遍历
            f[i][j] = f[i - 1][j];
            if (j >= v[i]) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
        }
    //前0个物品中选,体积是0的情况下,最大价值就是0,一种方案
    g[0][0] = 1;

    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            //查看一下当前的最大值,从前面哪个状态转移而来
            if (f[i][j] == f[i - 1][j])
                g[i][j] = (g[i][j] + g[i - 1][j]) % MOD;//叠加方案数

            if (j >= v[i] && f[i][j] == f[i - 1][j - v[i]] + w[i])
                g[i][j] = (g[i][j] + g[i - 1][j - v[i]]) % MOD;
        }
    }
    int res = 0;
    for (int i = 1; i <= m; i++)//遍历每一个体积
        if (f[n][i] == f[n][m]) res = (res + g[n][i]) % MOD;

    cout << res << endl;
    return 0;
}

三、一维实现

#include <bits/stdc++.h>

using namespace std;

const int N = 1010;
const int MOD = 1e9 + 7;

int f[N];   //f[i]用来存储背包容积为i时的最大价值,
int g[N];   //g[i]用来存储背包容积为i时,获取到最大价值时的方案数

int main() {
    //优化输入
    ios::sync_with_stdio(false);
    int n, m;
    cin >> n >> m;
    //先初始化所有的 g[i]为 1,因为背包里什么也不装也是一种方案,方案数最小是1
    for (int i = 0; i <= m; i++) g[i] = 1;

    for (int i = 1; i <= n; i++) {
        int v, w;
        cin >> v >> w;
        for (int j = m; j >= v; j--) {
            //求出装新物品时的总价值,与不装新物品时作对比
            int value = f[j - v] + w;
            //如果装新物品的总价值更大,那么用f[j−v]+w来更新f[j](经典01背包)
            if (value > f[j]) {
                f[j] = value;
                //用g[j−v]更新g[j]
                g[j] = g[j - v];
            } else if (value == f[j])
                //如果总价值相等,那么最大价值的方案数就多了 g[j−v]种
                g[j] = (g[j] + g[j - v]) % MOD;
        }
    }
    printf("%d", g[m]);
    return 0;
}

相关文章: