[BZOJ 1801] [Ahoi2009]chess 中国象棋 【DP】
题目链接:BZOJ - 1801
题目分析
对于50%的数据是可以直接状压 DP 的。
对于100%的数据,使用递推的 DP 。(或者这只叫递推不叫 DP ?)
可以发现,每一行和每一列的棋子个数不能超过 2 个。
用 f[i][j][k] 表示前 i 行,有 j 列有 1 个棋子,有 k 列有 2 个棋子的方案数。(有 0 个棋子的列数可以用 m - j - k 得到。)
初始状态 f[0][0][0] = 1;
转移时从第 i - 1 行转移过来,由于一行最多放 2 个棋子,所以可以讨论各种情况,转移一下。
代码
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; const int MaxN = 100 + 5; typedef long long LL; const LL Mod = 9999973; int n, m; LL Ans; LL f[MaxN][MaxN][MaxN]; LL C2(int a) { LL ret; ret = (LL)a * (LL)(a - 1) / 2ll % Mod; return ret; } int main() { scanf("%d%d", &n, &m); f[0][0][0] = 1; Ans = 0ll; for (int i = 1; i <= n; ++i) { for (int j = 0; j <= m; ++j) { for (int k = 0; k <= m - j; ++k) { f[i][j][k] = f[i - 1][j][k]; if (j >= 1) f[i][j][k] = (f[i][j][k] + (f[i - 1][j - 1][k] * (LL)(m - (j - 1) - k) % Mod)) % Mod; if (k >= 1) f[i][j][k] = (f[i][j][k] + (f[i - 1][j + 1][k - 1] * (LL)(j + 1) % Mod)) % Mod; if (j >= 2) f[i][j][k] = (f[i][j][k] + (f[i - 1][j - 2][k] * C2(m - (j - 2) - k) % Mod)) % Mod; if (k >= 2) f[i][j][k] = (f[i][j][k] + (f[i - 1][j + 2][k - 2] * C2(j + 2) % Mod)) % Mod; if (j >= 1 && k >= 1) f[i][j][k] = (f[i][j][k] + (f[i - 1][j][k - 1]) * (LL)(j) * (LL)(m - j - (k - 1)) % Mod) % Mod; if (i == n) Ans = (Ans + f[i][j][k]) % Mod; } } } printf("%lld\n", Ans); return 0; }