1. 程式人生 > >【狀壓dp】互不侵犯KING

【狀壓dp】互不侵犯KING

git algorithm long long true 求解 格子 ble bool span

互不侵犯KING

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

試題分析:狀壓dp,設dp[i][j][k]代表i*i的矩形放j個國王,此行狀態為k的二進制的種數

那麽容易得到轉移方程:dp[i][j][k]+=dp[i-1][j-cnt[k]][p]

其中cnt[k]表示k在二進制下1的數量,p表示枚舉的上一行的狀態

代碼

/*bzoj 1087
   wxjor 2017.06.06
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<stack>
#include<vector>
#include<algorithm>
//#include<cmath>

using namespace std;
const int INF = 9999999;
#define LL long long

inline int read(){
	int x=0,f=1;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1;
	for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘;
	return x*f;
}
int N,K;
long long dp[10][100][1025];
int cansr[1025];
int tmp;
int cnt[1025];
void pre(){//預處理所有可行狀態(在一行中KING互補侵犯)
	bool flag=true;
	for(int i=0;i<(1<<N);i++){
		int a=0,sum=0;
		flag=true;
		int p=i;
		while(i){
			if((i&1)&&a){
				flag=false;
				break;
			}
			a=(i&1);
			if(a) sum++;
			i>>=1;
		}
		if(flag) cansr[++tmp]=p,cnt[tmp]=sum,dp[1][sum][p]=1;//計入
		i=p;
	}
	return ;
} 
bool check(int a,int b){//判斷兩行中是否會侵犯
	if((a&b)||((a>>1)&b)||((a<<1)&b)||((b<<1)&a)||((b>>1)&a)) return false;
	return true;
}
long long ans;
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	N=read(),K=read();
	pre();
	for(int i=2;i<=N;i++){
		for(int j=0;j<=K;j++)//一開始寫成了j=1
		    for(int k=1;k<=tmp;k++){
		    	for(int p=1;p<=tmp;p++){
		    		if(!check(cansr[k],cansr[p])) continue;
		    		if(cnt[k]+cnt[p]>j) continue;//枚舉的狀態超出放的數量
		    		dp[i][j][cansr[k]]+=dp[i-1][j-cnt[k]][cansr[p]];
				}
			}
	}
	for(int i=1;i<=tmp;i++) ans+=dp[N][K][cansr[i]];//求解答案
	printf("%lld\n",ans);
	return 0;
}
//dp[i][j][k]+=dp[i-1][j-cnt(k)][k‘]

  

【狀壓dp】互不侵犯KING