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