BZOJ 1087 [SCOI2005]互不侵犯King

16 篇文章 0 订阅
16 篇文章 0 订阅

Description

  在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共8个格子。

Input

  只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

Output

  方案数。

Sample Input

3 2

Sample Output

16

分析

其实我们可以用一张美妙的表来解决这道题

mdzz,少了一个括号秘制调了一天。。。好贱啊。

先跑一边搜索,把所有的合法的状态跑出来,然后再跑一遍,把所有的合法的状态组合找出来,然后可以跑状压dp了,我们用F[i][j][k]表示前i行,共用了j个旗子,状态为k时的方案数。其中num[i]表示状态为i时用了多少棋子。

代码

/**************************************************************
    Problem: 1087
    User: ypxrain
    Language: C++
    Result: Accepted
    Time:72 ms
    Memory:10084 kb
****************************************************************/

#include <bits/stdc++.h>
#define N 1000
#define M 10

bool l[N];
bool map[N][N];
int num[N];
long long f[M][M*10][N];
long long ans,tot;

int n,k;

int read()
{
    int k=1,x=0;
    char ch;
    ch=getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch=='-')
            k=-1;
        ch=getchar();   
    }
    while(ch>='0' && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*k;
}

void initl()
{
    for(int i=0;i<tot;++i)
    {
        if((i&(i<<1))==0)
        {
            l[i]=true;
            int t=i,cnt=0;
            while(t)
            {
                cnt+=(t&1);
                t>>=1;
            }
            num[i]=cnt;
            f[1][cnt][i]=1;
        }
    }
}

void initm()
{
    for(int i=0;i<tot;++i)
    {
        if(!l[i])
            continue;
        for(int j=0;j<tot;++j)
        {
            if(!l[j])
                continue;
            if(!(i&j) && !((i<<1)&j) && !((i>>1)&j))    
                map[i][j]=true;
        }
    }
}

void dp()
{
    for(int i=2;i<=n;++i)
    {
        for(int j=0;j<=k;++j)
        {
            for(int now=0;now<tot;++now)
            {
                if(num[now]>j)
                    continue;
                if(!l[now]) 
                    continue;
                for(int nowk=0;nowk<tot;nowk++)
                {
                    if(!l[nowk])
                        continue;
                    if(!map[now][nowk])
                        continue;   
                    if(num[now]+num[nowk]>j)
                        continue;
                    f[i][j][now]+=f[i-1][j-num[now]][nowk]; 
                }
            }
        }
    }
}

void sum()
{
    for(int i=0;i<tot;++i)
    {
        if(!l[i])
            continue;
        ans+=f[n][k][i];
    }
}

int main()
{
    n=read();
    k=read();
    tot=1<<n;
    initl();
    initm();
    dp();
    sum();
    std::cout<<ans<<std::endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值