题目传送门

一、解题思路

把整数\(n\)看成一个容量为\(n\)的背包,有\(n\)种物品,物品的体积分别是\(1-n\),我们要求的是恰好装满背包的方案数(计数),每种物品可以用无限次,所以可以看成是一个完全背包。

先考虑二维的状态描述方法:

\(f[i,j]\):从前\(i\)中选,总和是\(j\)的选法,值就是方案数量。

(1)第\(i\)个物品选择了\(0\)
表达式:\(f[i-1,j]\) ,含义:在前\(i\)个物品中选择,结果现在第\(i\)个物品选择了\(0\)个,就是说这\(j\)个体积都是前\(i-1\)贡献的。

(2)第\(i\)个物品选择了\(1\)
表达式:\(f[i-1,j-1 * i]\) 含义:在前\(i\)个物品中选择,结果现在第\(i\)个物品选择了\(1\)个,就是说这 \(j-1*i\) 个体积都是前\(i-1\)贡献的。

(3)第\(i\)个物品选择了2个
\(f[i-1,j-2*i]\)

...
(4)第\(i\)个物品选择了s个
\(f[i-1,j-s*i]\)

初值问题

求最大值时,当都不选时,价值显然是 \(0\)

求方案数时,当都不选时,方案数是 \(1\)

即:

for (int i = 0; i <= n; i ++) f[i][0] = 1;

二、二维实现代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1010;
const int MOD = 1e9 + 7;
int n;
int f[N][N];

int main() {
    //优化输入
    ios::sync_with_stdio(false);
    cin >> n;
    //DP初始化
    for (int i = 0; i <= n; i++) f[i][0] = 1;

    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= n; j++) {//剩余的背包容量
            //可以装的下当前数字
            if (j >= i) {
                //放弃当前数字
                f[i][j] = f[i - 1][j] % MOD;
                //选择当前数字
                f[i][j] = (f[i - 1][j] + f[i][j - i]) % MOD;
            }
            //装不下当前数字,只能放弃
            else f[i][j] = f[i - 1][j] % MOD;
        }
    printf("%d", f[n][n]);
    return 0;
}

三、一维实现代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1010;
const int MOD = 1e9 + 7;
int f[N];
int n;

int main() {
    //优化输入
    ios::sync_with_stdio(false);
    cin >> n;
    f[0] = 1; // 容量为0时,前 i 个物品全不选也是一种方案
    for (int i = 1; i <= n; i++)
        for (int j = i; j <= n; j++)  //完全背包从小到大,01背包从大到小
            f[j] = (f[j] + f[j - i]) % MOD;
    printf("%d", f[n]);
    return 0;
}

相关文章:

  • 2021-11-20
  • 2022-12-23
  • 2021-07-12
  • 2022-03-05
  • 2021-11-14
  • 2022-01-27
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2021-06-06
  • 2022-01-19
相关资源
相似解决方案