1087: [SCOI2005]互不侵犯King (网格状压DP)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kidsummer/article/details/82391338

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

Input

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

Output

  方案数。

Sample Input

3 2

Sample Output

16

我们用  f[i][j][k] 代表,第 前 i 行, j 个国王,当前状态是 k ,一共有多少种方法。

我们先预处理出来哪些状态可以用。

从一个状态又可以到达哪些状态。

#include <bits/stdc++.h>
#define mem(x,v) memset(x,v,sizeof(x)) 
#define go(i,a,b)  for (int i = a; i <= b; i++)
#define og(i,a,b)  for (int i = a; i >= b; i--)
#define MID(a,b) (a + b) / 2
#define lson now << 1
#define rson now << 1 | 1
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const int INF = 0x3f3f3f3f;
const int N = 10;
LL f[N][N*N][1<<N];
int cnt[1<<N];
bool vis[1<<N],mp[1<<N][1<<N];
int n,m,all;
void Pre(){
	int s;
	go(i,0,all)
	if ((i&(i>>1))==0){ //右移一位,判断两个国王是否相邻。
		s = 0;
		for (int x = i; x; x>>=1) s += (x&1); //这个状态有多少个国王。
		cnt[i] = s; vis[i] = 1; //标记这个状态可行,
	}
	go(i,0,all) if (vis[i])
	go(j,0,all) if (vis[j])
	if ((i&j) == 0 && (i & (j>>1))==0 && (j & (i>>1))==0) mp[i][j]= 1; //判断两个状态是否可以到达。
}

int main(){
	scanf("%d%d",&n,&m);
	all = (1<<n) - 1;
	Pre();
	go(i,0,all) f[1][cnt[i]][i] = 1; //第一行的状态搞出来。
	go(j,1,n-1) //第几行
	go(k,0,all) if (vis[k]) //当前状态,
	go(i,0,all) if (vis[i]) //转移的状态。
	if (mp[k][i])
		for (int p = cnt[k]; p +cnt[i] <= m; p++) //枚举有多少国王,
			f[j+1][cnt[i]+p][i] += f[j][p][k]; //累加。状态转移。
	LL ans = 0;
	go(i,0,all) ans += f[n][m][i];
	cout<<ans<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/kidsummer/article/details/82391338