题意:用中国象棋中的规矩,在棋盘上摆炮要求不能被攻击,求方案数。
明显一列一行不能超过2个。
那么我们就可以以此转移,考虑一行行的来放,那么dp只跟行与行之间的关系有关。
设f[i][j][k]表示前i-1行做完了,现在做第i行,有j列只有一个棋子,k列只有两个棋子。
行的限制我们可以手动枚举,列的限制就放在dp里面去做。
那么有6种转移,至于如何实现,请看代码,这里不进行详写。
其实主要分为三大类。
一.放0个
直接继承
二.放1个
分别由原来没放棋子的列或者原来放了一个棋子的列分别转移而来。
三.放2个。
考虑这两个棋子放在哪里。
1.原来只有一个的列,原来没有棋子的列。
2.都放在原来没有棋子的列。
3.都放在原来只有一个棋子的列。
/喷血,如果是由i-1转移到i就没有问题,但是由i转移到i+1就会GG,可能有人能成功吧,因为转移以后j,k的范围变成了最多m+2,这导致有点难以统计,直接加起来是不对的,不知道该如何统计答案了。。有人知道的告诉我一下,WA了个爽。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int n,m;
const int N=115;
const int mo=9999973;
typedef long long ll;
//int C[N][N];
ll f[N][N][N];
inline void add(ll &x,ll y)
{
x=(x+y)%mo;
}
int main()
{
//pre();
scanf("%d%d",&n,&m);
memset(f,0,sizeof(f));
f[0][0][0]=1;
fo(i,1,n)
{
fo(j,0,m)
{
fo(k,0,m-j)
{
f[i][j][k]=f[i-1][j][k];
if (j>=1) (f[i][j][k]+=f[i-1][j-1][k]*(m-j-k+1))%=mo;
if (k>=1) (f[i][j][k]+=f[i-1][j+1][k-1]*(j+1))%=mo;
if (j>=2) (f[i][j][k]+=f[i-1][j-2][k]*(m-j-k+2)*(m-j-k+1)/2)%=mo;
if (k>=2) (f[i][j][k]+=f[i-1][j+2][k-2]*(j+2)*(j+1)/2)%=mo;
if (j>=1&&k>=1) (f[i][j][k]+=f[i-1][j][k-1]*(m-j-k+1)*j)%=mo;
}
}
}
ll ans=0;
fo(j,0,m)
fo(k,0,m-j)ans=(ans+1ll*f[n][j][k])%mo;
printf("%lld\n",ans);
}