集合:
f[i][j]
表示前i个整数(1,2…,i)恰好拼成j的方案数
状态计算:
求方案数:把集合选0个i,1个i,2个i,…全部加起来
f[i][j] = f[i - 1][j] + f[i - 1][j - i] + f[i - 1][j - 2 * i] + ...f[i - 1][s * j];
f[i][j - i] = f[i - 1][j - i] + f[i - 1][j - 2 * i] + ...f[i - 1][s * j];
因此 f[i][j]=f[i−1][j]+f[i][j−i];
(这一步类似完全背包的推导)
初值问题:
f[0] --> f[0][0]
表示总和是0,当一个都不选时,这就是一种方案,所以f[0] = 1
, 也就是f[0][0] = 1
f[1] --> f[0][1]
表示总和是1,当一个都不选时,这不是一种方案,实际上没有方案可以使得成立,所以f[1] = 0
,也就是f[0][1] = 0
f[2]
同f[1]
f[0][2]
f[i]
同f[1]
f[0][i]
(i > 0)
所以循环也就从 i = 1
开始
f[i][0] = 1;
(i>=0)表示背包容量为0,所有物品都不选,这是一种方案
优化前(优化了公式,没有优化维数)
// f[i][j] = f[i - 1][j] + f[i][j - i]
#include <iostream>
using namespace std;
const int N = 1e3 + 7, mod = 1e9 + 7;
int f[N][N];
int main() {
int n;
cin >> n;
for (int i = 0; i <= n; i ++) {
f[i][0] = 1; // 容量为0时,前 i 个物品全不选也是一种方案
}
for (int i = 1; i <= n; i ++) {
for (int j = 0; j <= n; j ++) {
f[i][j] = f[i - 1][j] % mod; // 特殊 f[0][0] = 1,一个i都不选
if (j >= i) f[i][j] = (f[i - 1][j] + f[i][j - i]) % mod; //选s个i的选法
}
}
cout << f[n][n] << endl;
}
优化后(维数优化)
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010, mod = 1e9 + 7;
int n;
int f[N];
int main () {
cin >> n;
f[0] = 1;
for (int i = 1; i <= n; i ++)
for (int j = i; j <= n; j ++)
f[j] = (f[j] + f[j - i]) % mod;
cout << f[n] << endl;
return 0;
}