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\) 列没有棋子。

考虑放棋子的个数:

  1. 不放时,贡献为 \(f_{i-1,j,k}\)
  2. 放一个时:
    • 放到没有棋子的一列,会使棋子为 \(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)\)
  3. 放两个时:
    • 都放到没有棋子的列,贡献为 \(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 
#include 
#include 
#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;
}

你可能感兴趣的:(P2051 [AHOI2009]中国象棋)