DP搬运工之搬运工1

longdie

2020-12-31 14:48:22

Personal

**注:以下系列题目为搬运 mengbier(yxy) 的DP经典题目。** # 题目 给你 $n(n<=3000)$, $K(K<=3000)$, 求有多少个$1到n$ 的排列, 恰有 $K$ 个数 $i(1<i<n)$ 满足 $a_{i-1}, a_{i+1}$ 都小于 $a_i$,输出答案 $mod 20172019$ 即可。 # 分析 本题其实是最简单的一道。 我们考虑从小到大加入数 $i$,那么只要 $i$ 放在任意两个数中间都会增加一个新的贡献。 那么我们可以定义 $f[i][j]$ 为考虑完 $1 - i$, 有 $j$ 对满足要求的方案数。 考虑转移,我们可以以$j$是否增加来转移。 这样分两种情况即可。 1. j不增加 那么如果i放在任意一个满足条件的数中间,都会减少一对, 并且会增加一对, 所以不增不减。 还有如果i放在两边,也会不增加。 2. j增加 除了不增加的地方,别的地方都增加。 时间复杂度$O(nK)$。 详解看代码。 ## 代码 ``` #include <bits/stdc++.h> using namespace std; const int mod = 20172019, N = 3005; int n, k, f[N][N]; int main() { cin >> n >> k; f[1][0] = 1, f[2][0] = 2;//初始化 for(int i = 3; i <= n; ++i) for(int j = 0; j <= min(k, i); ++j) f[i][j] = (f[i][j] + f[i-1][j] * 1ll * ((j + 1) << 1)) % mod, //j不增加 f[i][j + 1] = (f[i][j + 1] + f[i-1][j] * 1ll * (i-2 - (j << 1))) % mod;//j增加 cout << f[n][k] << '\n'; return 0; } ```