题意:在(N×N(N<=9)) 的棋盘里面放(M(M<=N*N)) 个国王,使他们互不攻击,共有多少种摆放方案?国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的一个格子.
分析:本题多了一个限制条件就是要求放m个国王.于是设(f[i][j][k]) 表示前i行,放置j个国王,且第i行状态为k的方案数.则(f[i][j][k]=f[i-1][j-num[k]][l]) (其中num[k]表示状态k中有多少位为1)((S[k]&S[l])=0且((S[k]<<1)&S[l])=0且((S[k]>>1)&S[l])=0且j-num[k]>=0)
### 老规矩,又可以麻烦地预处理出集合S,满足状态S[i]的任意两个为1的位不相邻.这样就可以直接枚举有用的状态,优化一下时间.(但其实好像没什么必要,时间复杂度没有优化多少,但是代码长度增加了十几行)
对于每个(S[i]) 我们还可以预处理出它的num[i],意义如上所述.
一定要记得开long long...
#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
int n,m,sum,S[1005],num[1005];
LL ans,f[10][100][1005];
inline int count(int x){
int cnt=0;
for(int i=0;i<n;i++)
if((x>>i)&1)cnt++;
return cnt;
}
int main(){
n=read(),m=read();
for(int i=0;i<(1<<n);i++){
int last=-1,bj=1;
for(int j=0;j<n;j++){
if((i>>j)&1){
if(last==-1)last=j;
else if(j-last<=1){
bj=0;
break;
}
else last=j;
}
}
if(bj)S[++sum]=i;
}
for(int i=1;i<=sum;i++){
num[i]=count(S[i]);
f[1][num[i]][S[i]]=1;
}
for(int i=2;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=1;k<=sum;k++)
for(int l=1;l<=sum;l++){
if((S[k]&S[l])||((S[k]<<1)&S[l])||((S[k]>>1)&S[l]))continue;
if(j-num[k]>=0)f[i][j][S[k]]+=f[i-1][j-num[k]][S[l]];
}
for(int i=1;i<=sum;i++)ans+=f[n][m][S[i]];
printf("%lld
",ans);
return 0;
}