BZOJ 1087: [SCOI2005]互不侵犯King 状态压缩 题解

Problem 1087. – [SCOI2005]互不侵犯King

1087: [SCOI2005]互不侵犯King

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 4028   Solved: 2338
[ Submit][ Status][ Discuss]

Description

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

左下右上右下八个方向上附近的各一个格子,共8个格子。

Input

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

Output

  方案数。

Sample Input


3 2

Sample Output


16

HINT


Source


[ Submit][ Status][ Discuss]


HOME
Back








而且貌似是一道状压的经典题
我们定义 f[i][cnt][zip] 表示对于第i层,该层及之前的层一共放了 cnt King ,然后第 i 层的状态是压缩成的情况的编号zip,显然这道题最后的答案是所有 f[n][k][] 的和,即表示第 n 层及以前的层,放了k King ,最后一层各种不同的放法的放法总和
状态转移方程也很好想,对于前一行的一种状态,如果我们可以转移到当前行的状态的话,有
f[i][cnt][now]+=f[i1][cntsize[now]][last]

其中 now 表示这一行的状态, last 表示上一行的状态, size[now] 表示这一行的这一种放法放了多少个 King 在这一行,如果可以转移的话,自然有这样的转移方程
我们还需要考虑如何预处理出每一行可以放的状态以及一种状态是否也可以转移到另一种状态,这个看代码应该就可以看懂了
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
const int MAXN=100+10;
const int MINN=10+1;
using namespace std;
int readin(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
bool judge[MAXN][MAXN];
int zip[MAXN],siz[MAXN],tail;
long long f[MINN][MAXN][MAXN];
int m,k;
void dfs(int num,int pos,int val){
    zip[++tail]=val;
    siz[tail]=num;
    if(num>=k||num>=((m+1)>>1)) return;
    for(register int i=pos+2;i<=m;i++) dfs(num+1,i,val+(1<<(i-1)));
}
int main(){
    //freopen(".txt","r",stdin);
    //freopen(".out","w",stdout);
    m=readin();k=readin();
    dfs(0,-1,0);
    for(register int i=1;i<=tail;i++){
        for(register int j=1;j<=tail;j++){
            if(zip[i]&zip[j]||(zip[i]<<1)&zip[j]||(zip[i]>>1)&zip[j]) judge[i][j]=judge[j][i]=false;
            else judge[i][j]=judge[j][i]=true; 
        }
    }
    for(register int i=1;i<=tail;i++) f[1][siz[i]][i]=1ll;
    for(register int i=2;i<=m;i++){
        for(register int j=0;j<=k;j++){
            for(register int last=1;last<=tail;last++){
                if (siz[last]>j) continue;
                for(register int now=1;now<=tail;now++){
                    if(judge[last][now]&&siz[last]+siz[now]<=j) f[i][j][now]+=f[i-1][j-siz[now]][last];
                }
            }
        }
    }
    long long final=0;
    for(register int i=1;i<=tail;i++) final+=f[m][k][i];
    printf("%lld\n",final);
    return 0;
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值