题意:
两个车互不攻击,当且仅当它们不在同一行或同一列上。输入整数n和k,你需要求出在n*n的国际象棋棋盘上放k个互不攻击的车有多少种方法。
tip:
你一个N*N的棋盘,要你在其中放入K个国王,每个国王会攻击到以它为中心的九宫格的相邻8个位置,求方案总数.
用F [ i ] [ j ] [ k ]来表示前i行放入k个国王,并且第 i 行的状态为第 j 个状态那么F[ i ] [ j ] [ k ] += F [ i-1][ p ][ k - c[i]],也就是对于前i-1行,我们枚举第i-1行的所有可能状态,
并且保证状态 j和状态p没有同一位放置国王(可以用位运算来完成),
由于这个国王可以攻击8个周围的点,所以我们有必要将i-1行的状态进行左移和右移的操作,来进行判断。先判断改行是否可以以这个状态放
c[i]就是j中二进制位为1的个数。。
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
const LL maxn = 11;
LL n,k,dp[maxn][1<<maxn][maxn*maxn];
LL line(LL x){
if((x>>1) & x) return 0;
return 1;
}
LL num(LL x){
LL cnt = 0;
while(x){
if(x&1) cnt++;
x >>= 1;
}
return cnt;
}
void init(){
memset(dp,0,sizeof(dp));
for(LL i = 0 ; i < (1<<n) ; i++){
if(line(i)){
dp[1][i][num(i)] = 1;
}
}
}
LL check(LL x,LL y){
if(x & y) return 0;
if((x>>1) & y) return 0;
if((x<<1) & y) return 0;
return 1;
}
void sov(){
for(LL i = 2; i <= n ; i++){
for(LL s_now = 0; s_now < (1 << n) ; s_now++){
if(!line(s_now)) continue;
LL x = num(s_now);
for(LL s_pre = 0 ; s_pre < (1<<n) ; s_pre++){
if(!line(s_pre)) continue;
for(LL r = x; r <= k; r++){
if(check(s_now,s_pre))
dp[i][s_now][r] += dp[i-1][s_pre][r-x];
}
}
}
}
LL ans = 0;
for(LL i = 0 ; i < (1<<n) ; i++){
ans += dp[n][i][k];
}
printf("%lld\n",ans);
}
int main(){
while(~scanf("%lld%lld",&n,&k)){
init();
sov();
}
}