ATC DP M Candies

ATC DP M Candies

https://atcoder.jp/contests/dp/tasks/dp_m

d p [ i ] [ j ] dp[i][j] dp[i][j] 代表到第 i 个人为止 吃了 j 颗糖的方案数

一个朴素的转移方程是

// 第一层循环遍历孩子
// 第二层循环遍历所有可能的糖果数量(上一层的)
// 第三层循环是遍历这些孩子取1-a[i](取0就是上一层一样)后从上一层的dp[j] 直接转移过来
for (int i=1;i<=n;++i) {
  for (int j=k;~j;--j)
    for (int l=j+1;l<=j+a[i];++l) 
      dp[l]=(dp[l]+dp[j])%mod;
}
// 技巧1: 用滚动数组这个参考背包就好了,这样保证前面用的都是上一层的值
// 技巧2: 里面两个循环的套路就是给数组中的一段加上一个定制,用前缀和差分就够了。这样降了n的复杂度。那么新的dp就放在最终代码里了。

总结一下就是,逐个增加dp的可以考虑滚动数组。对于持续要给某一段加定值的,考虑前缀和差分。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 1e6;
const ll mod = 1e9 + 7;
int dp[N];
int sum[N];
 
void add(int &a, int b) {
    a += b;
    if (a >= mod) {
        a -= mod;
    }
}
 
void sub(int &a, int b) {
    a -= b;
    if (a < 0) {
        a += mod;
    }
}
 
int main() {
    int n, k;
    cin >> n >> k;
    dp[0] = 1;
 
    for (int i = 1; i <= n; i++) {
        int a;
        cin >> a;
        memset(sum, 0, sizeof(sum));
        for (int j = k; j >= 0; j--) {
            int tem = dp[j];
            int l = j + 1;
            int r = j + (k - j, a);
            if (l <= r) {
                add(sum[l], tem);
                if (r + 1 <= k) {
                    sub(sum[r + 1], tem);
                }
            }
        }
        int cur = 0;
        for (int j = 0; j <= k; j++) {
            add(cur, sum[j]);
            add(dp[j], cur);
        }
    }
    printf("%d\n", dp[k]);
 
}

特别感谢https://blog.csdn.net/qq_31908675/article/details/118887126

看了半天才看懂,膜拜dalao.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值