Description
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共8个格子。
Input
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
Output
方案数。
Sample Input
3 2
Sample Output
16
题解:
题目好短,我喜欢。
是非常经典的,在棋盘上进行的状压dp。首先,两个王不能挨在一起,所以“11”型是不合法的。做一遍预处理,记录一行的合法情况,与两行的合法情况。用dp[i][j][k]表示放到了第i行,放了j个王,当前行状态为k的情况数,枚举状态转移就好了。记得答案要long long记录。
代码如下:
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#define ll long long
#define inf 0x7f7f7f7f
#define M 600
using namespace std;
int n,m,state;
bool c1[M],c2[M][M];
ll dp[11][120][M],cnt[M];
void pre()
{
for(int i=0;i<state;i++)
{
if(i&(i>>1)) continue;
for(int j=i;j>0;j>>=1) cnt[i]+=(j&1);
c1[i]=1;
}
for(int i=0;i<state;i++)
{
if(!c1[i]) continue;
dp[1][cnt[i]][i]=1;
for(int j=0;j<state;j++)
{
if(!c1[j] || (i&j) || (i&(j>>1)) || ((i>>1)&j)) continue;
c2[i][j]=1;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
state=(1<<n);
pre();
for(int i=2;i<=n;i++)
for(int j=0;j<state;j++)
{
if(!c1[j]) continue;
for(int k=0;k<state;k++)
{
if(!c1[k] || !c2[j][k]) continue;
for(int l=cnt[k];l<=m-cnt[j];l++)
dp[i][l+cnt[j]][j]+=dp[i-1][l][k];
}
}
ll ans=0;
for(int i=0;i<state;i++) ans+=dp[n][m][i];
printf("%lld\n",ans);
return 0;
}