【解题报告】[SCOI 2005] 互不侵犯

【省选系列】SCOI 2005 互不侵犯

Upd in 2022/9/27

修复了传送门链接

机器猫の传送门

简要题意

这题目够简要的了,所以略

题目分析

好看の数据范围

1<=N<=9, 0<=K<=N*N

这么小的数据范围,还是统计方案数,要么暴搜要么DP

搜索是没有出路滴,所以我们考虑DP

根据数据范围易得,可以通过状压解决

设状态

对于第i行,我们可以从上向下搞,也就是只考虑这一行左右和i-1行带来的影响,这样从上向下推下去,则i+1行对第i行的影响可以转化第i行为i对i+1行的影响,就可以进行转移了

我们不妨设一个三维的DP数组,DP[i][j][k]表示第i行的状态为j,放置k个国王的方案数,然后枚举i,j和i-1行的状态进行转移(状态j中的1表示放了棋子,状态j中的0表示没有棋子)

状态转移

用cnt[j]表示j中1的个数,即第i行放了cnt[j]个棋子

枚举k,则i-1行以前的个数为k-cnt[j],可得

dp[i][j][k]=dp[i][j][k]+dp[i-1][上一行的状态][k-cnt[j]] 边界状态 dp[0][0][0]=1

AC 代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<algorithm>
#define int long long

using namespace std;

const int N=10;

const int K=100;

const int maxn=(1<<9)+5;

int n,m,ans;

int dp[N][maxn][K];

inline int count(int x)
{
	int cnt=0;
	for(;x;x>>=1)
	{
		if(x&1)
		{
			cnt++;
		}
	}
	return cnt;
}

inline bool check(int x)
{
	if(((x<<1) & x) || ((x>>1) & x))
	{
		return false;
	}
	return true;
}

signed main()
{
	scanf("%d%d",&n,&m);
	
	dp[0][0][0]=1;
	
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<(1<<n);j++)
		{
			if(check(j)==false)
			{
				continue;
			}
			for(int k=0;k<(1<<n);k++)
			{
				if(check(k)==false)
				{
					continue;
				}
				if((k & j) || (j & (k>>1)) || (j & (k<<1)))
				{
					continue;
				}
				int cnt=count(j);
				for(int w=cnt;w<=m;w++)
				{
					dp[i][j][w]+=dp[i-1][k][w-cnt];
				}
			}
		}
	}
	
	for(int i=0;i<(1<<n);i++)
	{
		ans+=dp[n][i][m];
	}
	
	cout<<ans;
	
	return 0;
}
posted @ 2022-09-17 10:58  NinT_W  阅读(16)  评论(0编辑  收藏  举报