sgu223Little Kings(状压)

版权声明:转我原创记得说你是我的脑残粉哟 https://blog.csdn.net/zjy2015302395/article/details/77567148

题意:

两个车互不攻击,当且仅当它们不在同一行或同一列上。输入整数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();
    }
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页