BZOJ1087:[SCOI2005]互不侵犯——題解
阿新 • • 發佈:2018-01-08
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 2Sample 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; inlineint 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]互不侵犯——題解