题意

给定 \(n\) 行 \(m\) 列的棋盘,要求放若干个炮(可以是 \(0\) 个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。\((n, m\le 100)\)

题解

依次考虑每一行放哪些棋子,显然每一行最多放两个,所以可以讨论这 \(0\) ~ \(2\) 个棋子放置的位置。

而其实只需要知道这两个棋子放在了“已经有一个棋子”,“有两个棋子”,“没有棋子”中的哪一种列上。因为如果两种放置方法放在了相同种类的列上,那这两种放置方法就是等价的。

所以可以设 \(f_{i,j,k}\) 表示前 \(i\) 行有 \(j\) 列放了 \(1\) 个,\(k\) 列放了 \(2\) 个(则剩下 \(m-j-k\) 列都放了 \(0\) 个)的方案数。

讨论当前棋子的放置,先自己思考,点击可查看答案


  1. 不放,\(f_{i+1,j,k}\) += \(f_{i,j,k}\)
  2. 放一个

  • 放在没有棋子的列,\(f_{i+1,j+1,k}\) += \(f_{i,j,k}×(m-j-k)\)
  • 放在一个棋子的列,\(f_{i+1,j-1,k+1}+=f_{i,j,k}×j\)
  • 放在两个棋子的列,在想啥嘞不能放在两个棋子的列

  1. 放两个

  • 一个+一个,\(f_{i+1,j-2,k+2}\) += \(f_{i,j,k}×C_j^2\)
  • 一个+没有,\(f_{i+1,j,k+1}\) += \(f_{i,j,k}×j×(m-j-k)\)
  • 没有+没有,\(f_{i+1,j+2,k}\) += \(f_{i,j,k}×C_{m-j-k}^2\)


注意: 判断是否是合理转移,如“两个都放在没有棋子的列”时要判断 \(m-j-k>=2\) 才能转移。


#include<bits/stdc++.h>
#define int long long
const int mod = 9999973;
int n, m;
int dp[105][105][105];

int C(int num) { // C(num, 2)
return num * (num - 1) / 2;
}

signed main() {
std::ios::sync_with_stdio(0); std::cin.tie(0); std::cout.tie(0);

std::cin >> n >> m;

dp[0][0][0] = 1;
for(int i = 0; i <= n; ++i) {
for(int j = 0; j <= m; ++j) {
for(int k = 0; j + k <= m; ++k) {
if(dp[i][j][k]) {
/* 放2个 */
if(m - j - k >= 2) (dp[i + 1][j + 2][k] += dp[i][j][k] * C(m - j - k)) %= mod;
if(j >= 2) (dp[i + 1][j - 2][k + 2] += dp[i][j][k] * C(j)) %= mod;
if(j >= 1 && m - j - k >= 1) (dp[i + 1][j][k + 1] += dp[i][j][k] * (m - j - k) % mod * j % mod) %= mod;

/* 放1个 */
if(m - j - k >= 1) (dp[i + 1][j + 1][k] += dp[i][j][k] * (m - j - k)) %= mod;
if(j >= 1) (dp[i + 1][j - 1][k + 1] += dp[i][j][k] * j) %= mod;

/* 不放 */
(dp[i + 1][j][k] += dp[i][j][k]) %= mod;
}
}
}
}

int ans = 0;
for(int j = 0; j <= m; ++j) {
for(int k = 0; k + j <= m; ++k) {
ans += dp[n][j][k];
ans %= mod;
}
}
std::cout << ans;

return 0;
}