在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
注:数据有加强(2018/4/25)
输入格式
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
输出格式
所得的方案数
输入输出样例
输入 #1复制
3 2
输出 #1复制
16
题面很简单,但是这个题不能像n皇后那样去爆搜,我们的皇后问题可以爆搜是应为一个皇后放好后,我们就不会再考虑当前行了和当前列了,直接到下一行进行操作。这个国王问题一行可以放多个国王,一列也可以放多个。
题目的范围很小,可以采用状压dp。(1 << 9) = 512,就算是开三维也开的下。
先来分析题目,这题很类似摆放瓷砖那道题(poj 2411 没做过的小伙伴可以先尝试),也只用考虑上一行对当前行的影响。
由于国王对下一行的影响只有下面的三个格子。所以我们可以移位后按位 &。
例:第i行的摆放情况:k=0100 i+1:k1=0100
当前 k&k1 = 1
k1 = 1000 时 k&k1 = 1。 k1 = 0010 时同理。那么我们发现了只要 k&k1 ,k & (k1>>1),k & (k1 << 1)。这三个情况都为0,那么当前位置是可放国王的
详情见代码注释
#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <stdio.h>
#include <ctype.h>
#include <bitset>
#define LL long long
#define ULL unsigned long long
#define mod 10007
#define INF 0x7ffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define MODD(a,b) (((a%b)+b)%b)
//#define maxn 50
using namespace std;
const int maxn = (1 << 9) + 5;
int n,m;
LL dp[maxn][maxn][maxn];
int vis[maxn];
int num[maxn];
//初始化第一行的dp
void init()
{
for(int i = 0; i < (1 << n); i++){
if(!(i & (i >> 1))){//都在第一行放国王,只要满足不相邻即可
int k = i;
while(k){
num[i] += (k & 1);//算出在i状态下摆了多少个国王
k >>= 1;
}
vis[i] = 1;//这里的vis数组是必要的,记录是每一行的所有可行解
dp[1][num[i]][i] = 1;
}
}
}
void solve()
{
int p = (1 << n);
for(int i = 1; i <= n; i++){//从第一行开始遍历到第n行
for(int j = 0; j < p; j++){//上一行的状态
if(vis[j])
for(int k = 0; k < p; k++){//本行的状态
if(vis[k])
if(!(j & k) && (!(j & (k >> 1))) && (!(j & (k << 1)))){//满足条件
for(int temp = num[j];num[k] + temp <= m; temp++){//尝试多放几个国王,k状态的下的当前行的
dp[i + 1][temp + num[k]][k] += dp[i][temp][j];
}
}
}
}
}
LL ans = 0;
for(int i = 0; i < p; i++) ans += dp[n][m][i];
printf("%lld\n",ans);
}
int main()
{
scanf("%d%d",&n,&m);
init();
solve();
return 0;
}