bzoj1087
题目描述
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
输入格式
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
输出格式
一行,一个整数,表示答案。
样例数据
input
3 2
output
16
数据规模与约定
N<=10
这道题也是比较典型的状态压缩题,以f[i][j][k]表示在第k行的第i个状态下,剩余i个棋子要放的方案数。那么我们预处理出所有的合法状况,存到一个数组中,再开个布尔数组check[i][j]表示i和j能否作为相邻的两行。然后只要递推就行了,把下一行符合情况的f[][][]全部累加到当前的f中,唯一要注意的是f中的第一个变量代表的是状态的编号还是状态要确定,假如打的时候打错了可能就要陷入调试了……
AC代码如下:(代码可能有点冗长,大佬轻喷0.0)
#include<bits/stdc++.h>
using namespace std;
long long f[100][90][10];//f[i][j][k]表示 第k行,i状态,还剩余j个棋子的方案数
bool ff[100][90][10];
int n,m,now[100],q,sum[100];//now数组存的是每一个合法状态,q是数量 ,sum存的是状态对应含有1数量
bool hy[20],check[100][100];//check[i][j]表示两个状态能否相邻
int mi(int num,int sum){if(num==0) return sum;sum*=2;num--;return mi(num,sum);}//2^n
int read()
{
bool flag=true;
int num=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false;
for(;c>='0'&&c<='9';c=getchar())
num=(num<<3)+(num<<1)+c-48;
if(flag) return num;
else return -num;
}
void first()
{
for(int i=0;i<=mi(n,1)-1;++i)
{
memset(hy,0,sizeof(hy));
int j=1,num=i;
bool flag=false;
for(int k=mi(n-1,1);k>=1;k/=2,++j)
{
if(k<=num&&hy[j-1]) {flag=true;continue;}
if(k<=num) num-=k,hy[j]=true;
}
if(flag) continue;
q++;
for(int k=1;k<=n;++k)
if(hy[k]) now[q]+=mi(n-k,1);
}
memset(hy,0,sizeof(hy));
for(int i=1;i<=q;++i)
for(int j=i+1;j<=q;++j)
{
int num=now[i]|now[j];
bool chec=true;
if((now[i]&now[j])!=0) continue;
while(num>0)
{
bool flag=true;
if(num%2==1) flag=false;
num/=2;
if(num%2==1&&flag==false) {chec=false;break;}
}
if(chec) check[i][j]=check[j][i]=true;
}
check[1][1]=true;
for(int i=1;i<=q;++i)
{
int num=now[i];
while(num>0)
{
if(num%2==1) sum[i]++;
num/=2;
}
}
return;
}
void dp(int zh,int rest,int hang)
{
ff[zh][rest][hang]=true;
if(rest==0) return;
for(int i=1;i<=q;++i)
{
if(!check[zh][i]) continue;
if(rest-sum[i]<0) continue;
if(f[i][rest-sum[i]][hang+1]==-1) continue;
if(f[i][rest-sum[i]][hang+1]==0&&!ff[i][rest-sum[i]][hang+1]) {dp(i,rest-sum[i],hang+1),f[zh][rest][hang]+=f[i][rest-sum[i]][hang+1];}
else {f[zh][rest][hang]+=f[i][rest-sum[i]][hang+1];}
}
}
void work()
{
memset(f,0,sizeof(f));
for(int i=1;i<=q;++i) for(int j=1;j<=n;++j) f[i][0][j]=1,f[i][0][j]=true;
for(int i=1;i<=q;++i) for(int j=1;j<=m;++j) f[i][j][n]=-1;
long long ans=0;
for(int i=1;i<=q;++i) dp(i,m-sum[i],1),ans+=f[i][m-sum[i]][1];
printf("%lld",ans);
return;
}
int main()
{
n=read();m=read();
first();
work();
return 0;
}