P1896 [SCOI2005]互不侵犯 (状压dp)

题目连接

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

思路:设dp[ i ][ j ][ k ] 为第i 行摆放国王状态为 j ,棋盘上国王数目为k的方案数,那么首先处理出第一行的状态,跟矩阵相关的转压dp基本套路就是处理出第一行,然后枚举后面的行,枚举当前行前一行的状态,枚举当前行的状态,排除不满足条件的状态,转移就可以了,注意的是最后的答案是dp[ i ][ j ][ K ]的和且j!=0,因为j等于0的时候相当于重复计算了某些状态。

#include <bits/stdc++.h>
#define pi pair<int,int>
#define mk make_pair
#define ll long long
using namespace std;
const int maxn = 12;
int dp[maxn][1<<maxn][maxn*maxn];
int n,D;
int count(int s)
{
	int ans = 0;
	for(int i=0;i<n;i++)if((1<<i&s)!=0)++ans;
	return ans;
}
int main()
{
	scanf("%d%d",&n,&D);
	for(int i=0;i<1<<n;i++)
	if((i<<1&i)==0 && (i>>1&i)==0)dp[1][i][count(i)] = 1;
	for(int i=2;i<=n;i++)
	for(int s=0;s<1<<n;s++)
	for(int s0=0;s0<1<<n;s0++)
	{
		int c = count(s0);
		if((s0>>1&s0)==0 && (s0<<1&s0)==0 && (s0<<1&s)==0 && (s0&s)==0 && (s0>>1&s)==0)
		for(int k=0;c+k<=D;k++)
		{
			if(dp[i-1][s][k]==0)continue;
			dp[i][s0][c+k] += dp[i-1][s][k];
		}
	}
	ll ans = 0;
	for(int i=1;i<=n;i++)
	for(int j=1;j<1<<n;j++)ans += dp[i][j][D];
	cout<<ans<<'\n';
}

猜你喜欢

转载自blog.csdn.net/weixin_44499508/article/details/105410873
今日推荐