Description
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共8个格子。
Input
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
Output
方案数。
Sample Input
3 2
Sample Output
16
解题思路:
BZOJ上比较水的一个题目了,首先考虑一下搜索是肯定行不通的,因为状态实在是太多了,注意n的大小最多为8,我们可以考虑一下是否可以预处理排除一些状态,然后状压DP,这个实际上就是炮兵阵地的一个翻版了。我们dp[i][j][k] i代表行,j代表当前放了j个马,当前行状态为now,然后枚举转移即可。由于预处理了有效状态,所以DP的转移近乎于(1)的。具体细节在代码中都标注出来了,无坑点!
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i, a, b) for(int i = a; i <= b; i++)
#define uep(i, a, b) for(int i = a; i < b; i++)
const int maxn = 1<< 9;
int n, m, mask, cnt[maxn];
LL dp[10][100][maxn]; //dp[i][j][k] i代表行,j代表当前放了j个马,当前行状态为now
int f1[maxn], f2[maxn][maxn];
void pre_init(){
rep(i, 0, mask){
if((i&(i >> 1)) == 0){
f1[i] = 1;
cnt[i] = __builtin_popcount(i);
}
}
rep(i, 0, mask){
if(f1[i]){
rep(j, 0, mask){
if(f2[j]){
if(((i&j) == 0) && ((i&(j>>1)) == 0) && ((j & (i>>1)) == 0)){
f2[i][j] = 1;
}
}
}
}
}
}
int main(){
scanf("%d%d", &n, &m);
mask = (1 << n) - 1;
pre_init();
//初始化第一行DP值
rep(i, 0, mask) if(f1[i]) dp[1][cnt[i]][i] = 1;
for(int row = 1; row < n; row ++){
for(int prestate = 0; prestate <= mask; prestate++){
if(f1[prestate]){
for(int curstate = 0; curstate <= mask; curstate++){
if(f1[curstate]){
if(f2[prestate][curstate]){
for(int num = cnt[prestate]; num + cnt[curstate] <= m; num++){
dp[row + 1][num + cnt[curstate]][curstate] += dp[row][num][prestate];
}
}
}
}
}
}
}
LL ans = 0;
rep(i, 0, mask){
ans = ans + dp[n][m][i];
}
cout << ans << endl;
return 0;
}