P2051 [AHOI2009]中国象棋
知识点: DP
原题面
题意简述
给定一张 \(N\times M\) 的棋盘。
求每一行,每一列棋子数 \(<3\) 的方案数。
\(1\le N,M \le 100\)。
分析题意
合法的一行最多只有 \(2\) 个棋子,且只能放在棋子数 \(<2\) 的列上。
当枚举到第 \(i\) 行时,棋子数相等的两列是等价的,棋子的位置并没有影响。
考虑枚举 每种棋子数的列数,及这 \(2\) 个棋子的位置进行转移。
设 \(f_{i,j,k}\) 表示,前 \(i\) 行,有 \(j\) 列有 \(1\) 个棋子,\(k\) 列有两个棋子,合法的方案数。
显然有 \(m-j-k\) 列没有棋子。
考虑放棋子的个数:
- 不放时,贡献为 \(f_{i-1,j,k}\)。
- 放一个时:
- 放到没有棋子的一列,会使棋子为 \(1\) 的列数 + 1。
有 \(m-(j-1)-k\) 行没有棋子,则有这些种放置方案。
则贡献为 \(f_{i-1,j-1,k}\times (m-(j-1)-k)\)。 - 放到棋子数为 \(1\) 的一列,会使棋子为 \(1\) 的列数 - 1,会使棋子为 \(2\) 的列数 + 1。
有 \(j+1\) 种放置方案,则贡献为 \(f_{i-1,j+1,k-1}\times (j+1)\)。
- 放到没有棋子的一列,会使棋子为 \(1\) 的列数 + 1。
- 放两个时:
- 都放到没有棋子的列,贡献为 \(f_{i-1,j-2,k}\times C((m-(j-2)-k),2)\)。
- 都放在有 \(1\) 个棋子的列,贡献为 \(f_{i-1,j+2,k-2} \times C(j+2,2)\)。
- 一个放在有棋子的一列,一个放在有 \(1\) 个棋子的一列。
此时棋子数为 \(1\) 的列数不变。
贡献为 \(f_{i-1,j,k-1} \times j \times (m-(j-1)-k)\)。
贡献求和,即为 \(f_{i,j,k}\) 的值。
代码实现
//知识点:DP
/*
By:Luckyblock
*/
#include <cstdio>
#include <ctype.h>
#include <algorithm>
#define ll long long
const int kMaxn = 110;
const ll kMod = 9999973;
//=============================================================
ll n, m, ans, f[kMaxn][kMaxn][kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
ll C(ll x, ll y) {
return ((x - 1) * x / 2) % kMod;
}
//=============================================================
int main() {
n = read(), m = read();
f[0][0][0] = 1;
for (int i = 1; i <= n; ++ i) {
for (int j = 0; j <= m; ++ j) {
for (int k = 0; j + k <= m; ++ k) {
f[i][j][k] = f[i - 1][j][k];
if (k >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 1][k - 1] * (j + 1)) % kMod;
if (j >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k] * (m - j + 1 - k)) % kMod;
if (k >= 2) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 2][k - 2] * C(j + 2, 2)) % kMod;
if (j >= 2) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 2][k] * C(m - j + 2 - k, 2)) % kMod;
if (j >= 1 && k >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1] * j * (m - j - k + 1)) % kMod;
}
}
}
for (int j = 0; j <= m; ++ j) {
for (int k = 0; j + k <= m; ++ k) {
ans += f[n][j][k];
ans %= kMod;
}
}
printf("%lld", ans);
return 0;
}
作者@Luckyblock,转载请声明出处。