一道非常经典的完全背包求方案数题;
首先先写好我们的状态转移方程,那什么代表体积,什么代表物品数目呢;
其实很清晰k, k + 1 ... k+ n - 1 表示n个物品
m就是表示体积
这个就是完全背包问题之恰好体积为n的所有组合数
所以我们的状态表达式可以定义f[i][j] 前i个物品体积为j的所有方案数
状态转移方程:
f[i][j] = f[i - 1][j - i] + f[i - 1][j - 2 * i] + ... f[i - 1][j - n * i]
推理可知
f[i][j-i] = f[i - 1][j - 2 * i] + ... f[i - 1][j - n * i]
所以
f[i][j] = f[i - 1][j - i] + f[i][j - i]
由等比公式可知物品的数量最大为 (1 + n) * n / 2 = m // m 表示长度
所以时间复杂度为o(nm)
#include <iostream> #include <cmath> using namespace std; const int mod = 998244353; int add(int x, int y) { return (x + y > mod ? x + y - mod : x + y); } void solve() { int n,k; cin>>n>>k; int f[200010] = {0}, g[200010] = {0}, dp[200010] = {0}; // f 表示 f[i - 1][j - i] 的方案数 // g 表示 f[i][j - i] 的方案数 // dp 表示总方案数 f[0] = 1; // 根据完全背包定义可知,将体积为0的赋值为1 int m = sqrt(n / 2) + 10; for(int i = k; i <= k + m; i ++ ) { for(int j = i; j <= n; j ++ ) { g[j] = add(f[j - i], g[j - i]); dp[j] = add(dp[j], g[j]); } for(int l = 0; l <= n; l ++ ) f[l] = g[l], g[l] = 0; } for(int i = 1; i <= n; i ++ ) cout<<dp[i]<<" "; cout<<'\n'; } int main() { solve(); return 0; }