互不侵犯 (状压)

[SCOI2005] 互不侵犯

题目描述

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

输入格式

只有一行,包含两个数 \(N,K\)

输出格式

所得的方案数

样例 #1

样例输入 #1
3 2
样例输出 #1
16

提示

数据范围及约定

对于全部数据,\(1 \le N \le 9\)\(0 \le K \le N\times N\)

解析

状压板子题,把每一行压成一个二进制的状态,首先预处理行内所有可能的状态,

在不同行遍历的时候只考虑上一行的就可以了,

$ f[i][j][k] $ 表示在第 \(i\) 行,状态为 \(j\) ,已经放 \(k\) 个的方案数。

状压要敢于写循环吧,一眼数据范围,几层都炸不了。

#include<bits/stdc++.h>
using namespace std;
int n,k;
long long f[10][1<<10][100];
long long cnt[1<<10];
long long c[1<<10],num;
int main()
{
	scanf("%d%d",&n,&k);
	f[0][0][0]=1;
	for(int i=0;i<(1 << n);i++)
	{
		long long tot=0,j=i;
		while(j)
		{
			if(j & 1) tot++;
			j >>= 1;
		}
		cnt[i]=tot;
		if(!(((i<<1)|(i>>1)) & i)) c[++num]=i;
	}
	long long ans=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=num;j++)
		{
			long long s=c[j];
			for(int h=1;h<=num;h++)
			{
				long long s1=c[h];
				if(!((s1 | (s1<<1) | (s1>>1)) & s))
					for(int l=cnt[s];l<=k;l++)
						f[i][s][l]+=f[i-1][s1][l-cnt[s]];
			}
		}
	}
	for(int i=1;i<=num;i++) ans+=f[n][c[i]][k];
	printf("%lld",ans);
	return 0;
} 

例题

P2704 [NOI2001] 炮兵阵地

posted @ 2024-04-15 17:44  ppllxx_9G  阅读(14)  评论(0编辑  收藏  举报