【BZOJ1087】【SCOI2005】互不侵犯King
Description
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共8个格子。
Input
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
Output
方案数。
Sample Input
3 2
Sample Output
16
Solution
状压DP,\(f[i][j][S]\)表示前\(i\)行放\(j\)个棋子状态为\(S\)的方案数,枚举上一个转移的状态转移过来即可。
方程为$f[i][j][S]=\Sigma f[i-1][j-l][P] $(P为合法前驱状态,l为当前状态的1的个数).
时间复杂度\(O(nk2^{2n})\),记得跳过不合法与不必要的状态。
Code
#include <stdio.h>
#define MK 9
#define R register
#define ll long long
#define file(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
#define end fclose(stdin);fclose(stdout)
inline int read(){
R int x; R bool f; R char c;
for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
return f?-x:x;
}
ll f[MK+5][MK*MK+5][1<<MK],ans;int n,k;
inline int getlen(int S){
R int res=0;for (R int i=0; i<n; ++i)
res+=(bool)((1<<i)&S);return res;
}
inline bool check(int x,int y){
if (x&y) return 1;
if ((x>>1)&y) return 1;
if ((x<<1)&y) return 1;
return 0;
}
int main(){
n=read(),k=read();f[0][0][0]=1ll;
for (R int i=1; i<=n; ++i)
for (R int j=0; j<=k; ++j)
for (R int S=0; S<(1<<n); ++S){
R int l=getlen(S);
if (l>j) continue;
if (S&(S>>1)) continue;
if (S&(S<<1)) continue;
for (R int P=0; P<(1<<n); ++P){
if (!f[i-1][j-l][P]) continue;
if (check(S,P)) continue;
if (P&(P>>1)) continue;
if (P&(P<<1)) continue;
f[i][j][S]+=f[i-1][j-l][P];
}
}
for (R int S=0; S<(1<<n); ++S) ans+=f[n][k][S];
printf("%lld\n",ans);
return 0;
}