【每日一题】ABC248C - Dice Sum | 动态规划 |简单

题目内容

原题链接

构造一个长度为 n n n 的整数数组 a a a ,满足如下两个条件:

  • 1 ≤ a i ≤ m 1\leq a_i\leq m 1aim
  • ∑ a i ≤ k \sum a_i\leq k aik

问可以构造出多少种不同的数组 a a a

数据范围

  • 1 ≤ n , m ≤ 50 1\leq n,m\leq 50 1n,m50
  • n ≤ k ≤ n × m n\leq k\leq n\times m nkn×m

题解

经典的动态规划。

状态定义

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[i1][k],(1jkm)

状态转移只用到了第 i − 1 i-1 i1 的状态,故可以用两个数组滚动即可。

时间复杂度: 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;
}

你可能感兴趣的:(算法竞赛,动态规划,算法)