题意分析:
通过读题可知,本题的状态无非是在横向时,两个国王不能相邻,同时在列向,国王也不能相邻。二进制表示分别为与&和或|。
难点在于状态方程的建立 方案必须要用的每一行的状态,之后是行数,再其次是国王的数量(题目对国王的数量进行了限制),但是怎么去进行联系。仿照蒙德里安的梦想那一题,状态表示:就是前i行(i-1)的状态已经摆好,并且国王数量为j,第i行的状态为s的方案数的总和 状态方程:f[i][j][a]+=f[i-1][j-c][b];
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
typedef long long LL;
const int N=12,M=1<<10,K=110;
int n,m;
vector<int> state;
int cnt[M];
vector<int> head[M];
LL f[N][K][M]; //表示前i列已经摆好了j个国王,且第i行的状态为s的方案数
bool check(int state)
{
for(int i=0;i<n;i++)
if((state>>i&1)&&(state>>i+1 &1))
return false;
return true;
}
int count(int state)
{
int res=0;
for(int i=0;i<n;i++)
res+=state>>i&1;
return res;
}
int main()
{
cin>>n>>m;
//预处理
for(int i=0;i<1<<n;i++) //每一行的状态
if(check(i)) //判断国王不能相邻
{
state.push_back(i); //存入
cnt[i]=count(i); //统计在i的状态下,国王的数量。
}
//0101
//1010 1111
//枚举行和列的所有的状态,这时的状态已经满足了在行上不冲突
for(int i=0;i<state.size();i++)
for(int j=0;j<state.size();j++)
{
int a=state[i],b=state[j];
if((a&b)==0&&check(a|b))//不在同一行,不在同一列
head[i].push_back(j);
// 那么j对应状态,可以转移到i对应的状态。这里我们i,j都是状态对应的坐标
}
f[0][0][0]=1;
for(int i=1;i<=n+1;i++) //枚举行数
for(int j=0;j<=m;j++) //目前使用的国王棋子数量
for(int a=0;a<state.size();a++)//枚举一行中不冲突的方案
for(int b:head[a]) //枚举与合法状态不冲突的方案
{
int c=cnt[state[a]]; //国王的个数
if(j>=c)
f[i][j][a]+=f[i-1][j-c][b];
}
cout<<f[n+1][m][0]<<endl;
return 0;
}