[AHOI2009] 中国象棋

[AHOI2009] 中国象棋

蛮简单的一道 \(DP\) .

显然, 每行每列都最多只有 \(2\) 个炮, 所以我们设 \(f[i][j][k]\) 表示第 \(i\) 行, \(j\) 列有 \(1\) 个炮, \(k\) 列有 \(2\) 个炮的方案数.

然后转移就很简单了, 只需要枚举当前行放的一个或者两个炮放到哪里就行了.

像这种合法不合法只与单行或单列的数量有关的, 我们设状态就可以只存数量为多少的行或列的数量就可以了, 转移的时候用组合来计算我们共有多少种转移方案.

\(code:\)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read() {
  int x = 0, f = 1;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
  return x * f;
}

const int N = 105, mod = 9999973;

int n, m;
ll ans, f[N][N][N];

int main() {
  n = read(), m = read();
  f[0][0][0] = 1;
  for (int i = 1; i <= n; i++) {
    for (int j = m; j >= 0; j--) {
      for (int k = m - j; k >= 0; k--) {
        f[i][j][k] += f[i - 1][j][k];
        if (m - j - k) f[i][j + 1][k] = (f[i][j + 1][k] + f[i - 1][j][k] * (m - j - k)) % mod;
        if (m - j - k > 1) f[i][j + 2][k] = (f[i][j + 2][k] + (f[i - 1][j][k] * (m - j - k) * (m - j - k - 1) >> 1)) % mod;
        if (j) f[i][j - 1][k + 1] = (f[i][j - 1][k + 1] + f[i - 1][j][k] * j) % mod;
        if (j > 1) f[i][j - 2][k + 2] = (f[i][j - 2][k + 2] + (f[i - 1][j][k] * j * (j - 1) >> 1)) % mod;
        if (m - j - k && j) f[i][j][k + 1] = (f[i][j][k + 1] + f[i - 1][j][k] * j * (m - j - k)) % mod;
      }
    }
  }
  for (int i = 0; i <= m; i++) {
    for (int j = 0; j <= m - i; j++) {
      ans += f[n][i][j];
    }
  }
  printf("%lld", ans % mod);
  return 0;
}
posted @ 2021-09-26 15:58  sshadows  阅读(30)  评论(0编辑  收藏  举报