1. 程式人生 > >BZOJ1087:[SCOI2005]互不侵犯——題解

BZOJ1087:[SCOI2005]互不侵犯——題解

spa des lin div algo 16px size gpo 上下

http://www.lydsy.com/JudgeOnline/problem.php?id=1087

Description

  在N×N的棋盤裏面放K個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上
左下右上右下八個方向上附近的各一個格子,共8個格子。

Input

  只有一行,包含兩個數N,K ( 1 <=N <=9, 0 <= K <= N * N)

Output

  方案數。

Sample Input

3 2

Sample Output

16

——————————————————————————————

n很小,暴力太麻煩,考慮狀壓。

設f[i][j][k]表示前i行放j個國王且第i行排成k情況的時候的情況數有多少。

g[i]表示一行國王排成i情況的時候有幾個國王。

轉移的時候顯然是f[i][j][k]+=f[i-1][j-g[k]][l]

其中保證l合法,並且j最小值為g[k]+g[l]。

於是得到算法構架:

枚舉i,枚舉k,判斷k的合法性,枚舉l,判斷l的合法性,枚舉j,計算。

Q1:g怎麽算?

A1:求g[i],我們可以通過將i右移,然後判斷i最後一位為0還為1,所以答案為:g[i]=g[i>>1]+(i&1);

Q2:如何判斷狀態合法?

A2:我們判斷相鄰行i和j狀態之間是否合法,首先判斷i和j本身是否合法——通過將本身左/右移,再和原狀態&一下,如果不為0就一定撞上了。

再考慮i和j,同樣的思路,將j左/右移和i&,將i左/右移和j&,如果不為0就一定撞上了。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int N=512;
const int INF=2147483647;
inline 
int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch==-;ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } int g[N]; ll ans,f[10][100][N]; int main(){ int n,m; scanf("%d%d",&n,&m); if(m>25||m>=n*n)puts("0"); else{ int t=1<<n;f[0][0][0]=1; for(int i=1;i<t;i++)g[i]=g[i>>1]+(i&1); for(int i=1;i<=n;i++){ for(int j=0;j<t;j++){ if(g[j]<=m&&!(j&j>>1)){ for(int k=0;k<t;k++){ if(g[k]<=m&&!(k&k>>1)&&!(k&j)&&!(j&k>>1)&&!(j&k<<1)){ for(int l=g[j]+g[k];l<=m;l++){ f[i][l][j]+=f[i-1][l-g[j]][k]; } } } } } } for(int i=0;i<t;i++)ans+=f[n][m][i]; printf("%lld\n",ans); } return 0; }

BZOJ1087:[SCOI2005]互不侵犯——題解