DP搬运工之搬运工2
注:以下系列题目为搬运 mengbier(yxy) 的DP经典题目。
题目
给你
分析
这应该算是我做过的第一道预设型DP。 我们很显然的可以发现,如果从小到大考虑的话,会出现后效性,这就启发我们先预设后面的状态来进行DP和转移。 因为我们一般计算的是当前情况的贡献,我们可以通过限制后面情况来满足当前。
就本题而言:我们可以通过提前声明数
- 两边都不填,那么贡献会增加
2*x 。 - 填一边,那么会增加
x 的贡献。 - 都不填,那么不会增加贡献。
当然不要忘了讨论数放在端点上的情况。
我们可以定义
代码
#include <bits/stdc++.h>
using namespace std;
inline int read(int x = 0) { return scanf("%d", &x), x; }
const int mod = 20172019, N = 55;
int n, K, f[N][N][N*N], ans = 0;
int main() {
n = read(), K = read();
f[1][0][0] = 1;
for(int i = 2; i <= n; ++i)//采用刷表法进行DP
for(int j = 0; j <= i; ++j)
for(int k = 0; k <= K; ++k) {
int now = (f[i-1][j][k] * 2) % mod;//放在两端的情况。
f[i][j + 1][k] = (f[i][j + 1][k] + now) % mod;
f[i][j][k + i] = (f[i][j][k + i] + now) % mod;
now = f[i-1][j][k] * 1ll * j % mod;//放在中间的情况。
f[i][j + 1][k] = (f[i][j + 1][k] + now) % mod;
f[i][j][k + i] = (f[i][j][k + i] + now * 2ll) % mod;
f[i][j-1][k + 2*i] = (f[i][j-1][k + 2*i] + now) % mod;
}
for(int i = 0; i <= K; ++i) ans = (ans + f[n][0][i]) % mod;//统计答案。
return cout << ans, 0;
}