互不侵犯 (狀壓)

ppllxx發表於2024-04-15

[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] 炮兵陣地

相關文章