题意:给出n*n大小的棋盘,黑白两色相间,要在其中放m个相,问使之互不攻击的方案种数。
= ∑(白格子放k个,并且黑格子放m-k个的种数)
分别考虑黑白格子,对于棋盘进行变形,使得不能在斜角位置的条件 变为 不能在同行同列。
白格子的变形方法是:不考虑黑格子,将剩下的白格子顺时针旋转45度,然后压缩。(使得图形变为倒置的Ferres图像)。
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define all(x) (x).begin(), (x).end()
#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)
#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)
typedef long long ll;
typedef pair<int, int> pii;
const int INF =0x3f3f3f3f;
const int maxn= 8 ;
int n,m;
int h[2*maxn+3];//转化后每行的棋子数
int N;
int f(int x)
{
return x%2;
}
ll ans[2][80];
ll dp[2*maxn+3][80];//递推求解,并不是动归
void cal(int k)
{
memset(dp,0,sizeof dp);
dp[0][0]=1;//n为1的情况会用到
dp[1][0]=1;
dp[1][1]=h[1];//dp[n][x]表示转换后前n行,用了x个棋子。
for(int i=2;i<=N;i++)
{
dp[i][0]=1;
for(int j=1;j<=i;j++)
{
dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*(h[i]-(j-1));
}
}
for(int j=0;j<=m;j++)
{
ans[k][j]=dp[N][j];
}
}
void get(int k)
{
memset(h,0,sizeof h);
if(n%2==0)N= k?n:n-1;//如果是棋盘奇数行,那么在右上角颜色的方块转化后应该有这么多行。
else N= k?n-1:n;
for1(i,n) for1(j,n)
{
if( abs(f(i)-f(j) )==k )//判断该方块的行列数奇偶性是否相等,结果对应了两种颜色
h[N+1-min(n+1-i,j)]++;//对应到转换后的行,改行数量+1
}
cal(k);
/*
cout<<N<<endl;
for(int i=1;i<=N;i++)
{
cout<<i<<" "<<h[i]<<endl;
}*/
}
int main()
{
while(~scanf("%d%d",&n,&m)&&(n||m))
{
memset(ans,0,sizeof ans);
get(0);//getwhite
get(1);//getblack
ll reg=0;
for(int i=0;i<=m;i++)
{
reg+=ans[0][i]*ans[1][m-i];
}
printf("%lld\n",reg);
}
return 0;
}