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
",ans);
    return 0;
}
 
原文地址:https://www.cnblogs.com/candy99/p/6218769.html