7818 - 试题I:美味糖果 25

题目: 传送门
思路:
显而易见的想法就是暴力dp,dp[i][j]表示从前i个年货中取出j个的方案个数,那么应该有 d p [ i ] [ j ] = ∑ k = 0 a [ i ] d p [ i − 1 ] [ j − k ] , j − k > = 0 dp[i][j] = \sum_{k=0}^{a[i]}dp[i-1][j-k], j-k>=0 dp[i][j]=k=0a[i]dp[i1][jk],jk>=0

  • 由于数据量大,不能直接写二维dp,注意到状态转移方程求i时,只用到了i-1,故可以使用滚动数组(注意更新dp[j]的先后顺序,不能用被覆盖掉的上一轮的值来更新)
  • 又因为涉及到求和,直接写有 O ( n ∗ a [ i ] ∗ k ) O(n*a[i]*k) O(na[i]k)将近 1 0 13 10^{13} 1013的复杂度,我们对dp数组求前缀和,就可以在 O ( 1 ) O(1) O(1)的时间内求和,从而将复杂度降到了 O ( n ∗ a [ i ] ∗ 1 ) O(n*a[i]*1) O(na[i]1) 最大只有 1 0 8 10^8 108,就可以AC了

Code:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 100007;
const ll mod = 998244353;

ll dp[maxn];//(优化成一维滚动数组数组之前)dp[i][j]表示从前i个年货里取了j个的方案数
ll sum[maxn];//sum是dp数组的前缀和
int arr[maxn];
int main()
{
    int n, k;
    scanf("%d %d", &n, &k);
    for (int i=1;i<=n;i++) {
        scanf("%d", &arr[i]);
    }

    
    dp[0] = 1;
    sum[0] = 1;
    
    for (int i=1;i<=n;i++) {
        //先求前缀和
        for (int j=1;j<=k;j++) {
            sum[j] = (sum[j-1] + dp[j]) % mod;
        }
        //更新dp
        for (int j=k;j>=0;j--) {
            //求dp[j]+dp[j-1]+...+dp[j-arr[i]] 还要保证下标>=0
            dp[j] = (sum[j] - sum[j - min(j, arr[i]) - 1] + mod) % mod;
        }
        
    }
    ll ans = 0;
    for (int i=0;i<=k;i++) {
        ans = (ans + dp[i]) % mod;
    }
    printf("%lld\n", ans);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值