sgu223Little Kings(状压)

题意:

两个车互不攻击,当且仅当它们不在同一行或同一列上。输入整数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();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值