首页 > 代码库 > BZOJ 1087: [SCOI2005]互不侵犯King [状压DP]

BZOJ 1087: [SCOI2005]互不侵犯King [状压DP]

1087: [SCOI2005]互不侵犯King

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

orz popoqqq

状压DP,将每一行的方案二进制压成一维,令f[i][j][k]为第i行用去j个国王状态为k的方案数,然后状态转移如下:

f[i][j][k]=Σf[i-1][j-d[k]][l]

其中l&k=0,l>>1&k=0,l<<1&k=0,d[k]为k的二进制中1的个数

暴力转移即可

记得开long long

 

预处理来优化

需要判断自身状态可行,判断可行用了移位很巧妙!

 

////  main.cpp//  bzoj1087////  Created by Candy on 2016/12/24.//  Copyright © 2016年 Candy. All rights reserved.//#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;typedef long long ll;const int N=10,M=512;int read(){    char c=getchar();int x=0,f=1;    while(c<0||c>9){if(c==-)f=-1; c=getchar();}    while(c>=0&&c<=9){x=x*10+c-0; c=getchar();}    return x*f;}int n,m,s;int g[M][M],can[M],d[M];ll f[N][N*N][M];inline int digit(int x){    int re=0;    while(x) re+=x&1,x>>=1;    return re;}inline bool check(int x){return !(x<<1&x||x>>1&x);}inline bool check(int x,int y){    return !(x&y||x<<1&y||x>>1&y);}int main(int argc, const char * argv[]) {    n=read();m=read();s=1<<n;    for(int i=0;i<s;i++){        for(int j=i;j<s;j++)g[i][j]=g[j][i]=check(i,j);        d[i]=digit(i);        can[i]=check(i);    }    f[0][0][0]=1;    for(int i=1;i<=n;i++){        int t=min(m,i*n/2+1);        for(int j=0;j<=t;j++)            for(int k=0;k<s;k++) if(can[k]&&d[k]<=j)                for(int l=0;l<s;l++) if(can[l]&&g[k][l]&&d[k]+d[l]<=j)                    f[i][j][k]+=f[i-1][j-d[k]][l];    }    ll ans=0;    for(int i=0;i<s;i++) ans+=f[n][m][i];    printf("%lld\n",ans);    return 0;}

 

 

 

 

 

BZOJ 1087: [SCOI2005]互不侵犯King [状压DP]