有一个 的一个棋盘,问在棋盘上面摆放炮且炮与炮之间互不攻击的方案数。
一看就是DP。
看到 相对于状压DP的数据规模来说比较大,因此不好状压。
仔细分析,炮与炮之间发生冲突当且仅当所在的行或列上有 个及以上的炮。
而且,只要一列上的炮数少于 个,无论这两列摆在什么位置,这几个炮都不会相互攻击。由此,我们并不需要记录这几个炮的具体摆放位置,只要知道放有 个或 个炮的位置就行了,这样没有摆炮的列数也可以推出来。
因此,我们用 表示前 行有 列摆放了 个炮,有 列摆放了 个炮。
在第 行最多能放两个炮,因此分情况考虑写状态转移方程(状态转移计算均在取模的意义下进行)
不放炮
在没炮的地方放一个
在有一个炮的地方放一个
在两个没炮的地方各放一个
在没炮的地方和一个炮的地方各放一个
在两个有一个炮的地方各放一个
状态转移方程有点多,需要细心。
然后看一看这个状态转移方程,发现它只于之前的状态有关,我们就可以用滚动数组来优化空间。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 105
#define p 999983
#define calc(x) ((x) * ((x) - 1) / 2 % p)
long long d[2][MAXN][MAXN];
int main() {
int n, m, cur = 0;
scanf("%d%d", &n, &m);
d[0][0][0] = 1;
for (int i = 1; i <= n; i++) {
cur ^= 1;
for (int j = 0; j <= m; j++)
for (int k = 0; k + j <= m; k++) {
d[cur][j][k] = d[cur ^ 1][j][k];
if (j >= 1) d[cur][j][k] += d[cur ^ 1][j - 1][k] * (m - (j - 1) - k);
if (k >= 1) d[cur][j][k] += d[cur ^ 1][j + 1][k - 1] * (j + 1);
if (j >= 2) d[cur][j][k] += d[cur ^ 1][j - 2][k] * calc(m - (j - 2) - k);
if (k >= 1) d[cur][j][k] += d[cur ^ 1][j][k - 1] * (m - j - (k - 1)) * j;
if (k >= 2) d[cur][j][k] += d[cur ^ 1][j + 2][k - 2] * calc(j + 2);
d[cur][j][k] %= p;
}
}
long long ans = 0;
for (int i = 0; i <= m; i++)
for (int j = 0; i + j <= m; j++)
(ans += d[cur][i][j]) %= p;
printf("%lld\n", ans);
return 0;
}