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.