AcWing 278. 数字组合


题目传送门

一、题目解析

二、二维实现代码

#include 

using namespace std;
/**
二维数组+动态规划
状态转移方程:
1. 不选i:f[i][j] = f[i-1][j]
2. 选i: f[i][j] = f[i-1][j-v[i]]
所以总的方案数就1和2的和,注意:因为体积为0时,即j=0时,是存在一种方案的:f[0][0] = 1
*/
const int N = 110;
const int M = 10010;
int n, m;
int a[N];
int f[N][M];

int main() {
    cin >> n >> m;
    f[0][0] = 1;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        for (int j = 0; j <= m; j++) {
            //不管能不能选,都可以从上一个状态继承过来
            f[i][j] = f[i - 1][j];
            //如果可选,还要增加另一些状态的方案数
            if (j >= a[i]) f[i][j] += f[i - 1][j - a[i]];
        }
    }
    //输出结果
    printf("%d", f[n][m]);
    return 0;
}

三、一维实现代码

#include 

using namespace std;
const int N = 10010;

int n, m;
int a[N];
int f[N];
/*
转化为01背包问题求方案数:
将总和M看作背包容量;
将每个数v[i]看作体积为v[i] 的物品;

一维数组+动态规划
因为在解法一中,状态转移方程只使用到了i-1和j-v[i],所以对于二维数组来说,
其他记录的状态都是多余的,所以我们可以使用滚动数组来对解法一进行优化
状态转移方程:f[j] += f[j-v[i]]
注意:当变为f[j] += f[j-v[i]]后,对应的二维状态转移方程为:
f[j][j] += f[j][j-v[i]]和原二维转移方程矛盾,因为在顺序遍历过程中会导致i-1层的
数据先被覆盖,所以需要逆序遍历,这样就会先计算高层元素而不会影响底层元素
*/
int main() {
    cin >> n >> m;
    f[0] = 1;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        for (int j = m; j >= a[i]; j--)
            f[j] += f[j - a[i]];
    }
    printf("%d", f[m]);
    return 0;
}