BZOJ 1087 互不侵犯King

来源:互联网 发布:逆战数据怎么开启 编辑:程序博客网 时间:2024/05/16 03:23

1087: [SCOI2005]互不侵犯King

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 4064  Solved: 2354
[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

一道状压DP。

状压DP就是可以解决一些状态不易表达的问题。本题中将放国王与不放设置为0和1即可把每一行的状态变为一个01串,再对应二进制转十进制即可表达状态。设f[i][j][s]表示第i行共用了k个国王,且第i行摆法的二进制转十进制为s的方案总数。考虑两个问题,1.如何转移?枚举前一行的状态 2.如何判断状态是否合法?利用位运算的&字符,国王可以攻击八联通方向,而我们更新时只需要关注左上,正上,右上,左,右五个位置,若能攻击到,则水平方向相邻两个位置必有国王,所以我们只需要把两个状态&一下,若结果为0,证明两行不存在国王在国王的正上方,再将某一个左移一位,&一下,再右移一位,&一下,即可保证斜上方也不存在国王可以攻击。

细节有很多地方,需要保证枚举的状态合法,前一行的状态合法,故要有很多&操作。

#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<iostream>#include<cmath>#include<cstdlib>#include<ctime>using namespace std;const int MAXN=11;const int MAXM=520;long long f[MAXN][MAXN*MAXN][MAXM]; int main(){int n,k;memset(f,0,sizeof(f));scanf("%d%d",&n,&k);for (int i=0;i<=(1<<n)-1;i++){if ((i & (i>>1))==0){int x=i,tot=0;while (x){if (x & 1){tot++;}x/=2;}f[1][tot][i]=1;}}for (int i=2;i<=n;i++){for (int j=0;j<=(1<<n)-1;j++){for (int p=0;p<=(1<<n)-1;p++){for (int q=0;q<=k;q++){int x=j,y=p,tot1=0,tot2=0;if ((j&(p>>1))!=0){continue;}if ((j & p)!=0){continue;}if ((j&(p<<1))!=0){continue;}if ((j&(j>>1))!=0){continue;}if ((p&(p>>1))==0){while (x){if (x & 1){tot1++;}x/=2;}if (tot1+q>k){continue;}f[i][tot1+q][j]+=f[i-1][q][p];}}}}}long long ans=0;for (int i=0;i<=(1<<n)-1;i++){ans+=f[n][k][i];}cout<<ans;    return 0;}


原创粉丝点击