构造一个长度为 n n n 的整数数组 a a a ,满足如下两个条件:
问可以构造出多少种不同的数组 a a a 。
经典的动态规划。
d p [ i ] [ j ] dp[i][j] dp[i][j] 表示数组 a a a 的前 i i i 个数,和为 j j j 的方案数。
d p [ i ] [ j ] = ∑ d p [ i − 1 ] [ k ] , ( 1 ≤ j − k ≤ m ) dp[i][j]=\sum\limits dp[i-1][k],(1\leq j-k\leq m) dp[i][j]=∑dp[i−1][k],(1≤j−k≤m)
状态转移只用到了第 i − 1 i-1 i−1 的状态,故可以用两个数组滚动即可。
时间复杂度: O ( n m k ) O(nmk) O(nmk)
#include
using namespace std;
const int MOD = 998244353;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, k;
cin >> n >> m >> k;
// dp[i][j] 表示前 i 个数,和为 j 的方案数
// dp[i][j] = dp[i - 1][k], k < j
vector<int> dp(k + 1);
vector<int> ndp(k + 1);
dp[0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = i; j <= min(k, i * m); ++j) {
ndp[j] = 0;
// 最低是 i-1, 但是当前最多为 m ,所以 j-m
for (int ch = 1; ch <= m && j - ch >= i - 1; ++ch) {
ndp[j] += dp[j - ch];
if (ndp[j] >= MOD) ndp[j] -= MOD;
}
}
swap(dp, ndp);
}
int ans = 0;
for (int j = n; j <= min(n * m, k); ++j) {
ans += dp[j];
if (ans >= MOD) ans -= MOD;
}
cout << ans << "\n";
return 0;
}