看了题后,可以发现一行最多两个炮,一列最多两个炮。
做状压dp一般都是枚举行来做,所以一行两炮可以在一次for中枚举出来,但列上炮的数量不好计算。看了dalao的题解,发现状态这样设置
,
代表枚举的行数,
代表有
有1个棋子,
表示有
列有两个棋子
于是状态转移有
- 不放
- 放一个在:无棋子的列上(乘上没有棋子的个数) / 一个棋子的列上(乘上有一个棋子的个数)
- 放两个在:都在无棋子的列上(乘上
,
为没有棋子的列数) / 一个在一个棋子列上,一个在无棋子列上(乘上两种列数的个数) / 两个都在一个棋子列上 (乘上
,
为有一个棋子的列数)
详细看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline void read(int &x){
x = 0; int f = 1; char ch = getchar();
while (!(ch >= '0' && ch <= '9')){if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
inline void Max(int &x, int y){if (y > x) x = y;}
const int N = 101;
const int MOD = 9999973;
int n, m, ans;
int f[N][N][N];
inline int C(int x){return x * (x - 1) / 2;}
signed main(){
read(n), read(m);
f[0][0][0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= m; j++)
for (int k = 0; k + j <= m; k++) if (f[i - 1][j][k]){
(f[i][j][k] += f[i - 1][j][k]) % MOD;//不放
if (m - j - k >= 1) (f[i][j + 1][k] += f[i - 1][j][k] * (m - j - k)) %= MOD;
//放一个在无棋子的列上
if (j >= 1) (f[i][j - 1][k + 1] += f[i - 1][j][k] * j) %= MOD;
//放一个在有一个棋子的列上
if (m - j - k >= 2) (f[i][j + 2][k] += f[i - 1][j][k] * C(m - j - k)) %= MOD;
//放两个都在无棋子的列上
if (j >= 1 && m - j - k >= 1) (f[i][j][k + 1] += f[i - 1][j][k] * j * (m - j - k)) %= MOD;
//放两个一个在一个棋子列上,一个在无棋子列上
if (j >= 2) (f[i][j - 2][k + 2] += f[i - 1][j][k] * C(j)) %= MOD;
//放两个都在一个棋子列上
}
for (int i = 0; i <= m; i++)
for (int j = 0; i + j <= m; j++)
(ans += f[n][i][j]) %= MOD;
//把所有状态累计就是答案
printf("%lld\n", ans);
return 0;
}