一、解题思路
把整数\(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;
}