BZOJ1087 互不侵犯King¶
题面¶
在 N\times N 的棋盘里面放 K 个国王(N\leq 9),使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上 左下右上右下八个方向上附近的各一个格子,共 8 个格子。
题解¶
就是个简单的状压 DP 题,设状态 f[i][s][k]表示dp到第i行,这行的状态为s,前i行共放了k个国王的方案数。
每一行从上一行与所枚举状态不冲突的状态转移就行。
假设当前状态为 s ,上一行的 t 状态与它不冲突,那么可以有转移:
f[i][s][k]\ \ +=\ \ f[i - 1][t][k - cnt(s)]
cnt(s) 表示 s 这个状态中有多少个国王。
我用了滚动数组。
程序¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | #include <cstdio> #include <iostream> #include <algorithm> #include <cmath> #include <cstring> #define MAX_N 527 typedef long long ll; int N, K; ll f[2][MAX_N][82]; inline int read () { int x = 0; char c = getchar(); while (c < '0' || '9' < c) c = getchar(); while ('0' <= c && c <= '9') x = x * 10 + c - '0', c = getchar(); return x; } inline bool check (int x) { for (int i = 0;i < N - 1; ++i) { if (((x >> i) & 1) & ((x >> i + 1) & 1)) return false; } return true; } inline bool check2 (int x, int y) { for (int i = 0;i < N; ++i) { if (((x >> i) & 1) & ((y >> i) & 1)) return false; if (((x >> i) & 1) & ((y >> i + 1) & 1)) return false; } for (int i = 0;i < N; ++i) { if (((y >> i) & 1) & ((x >> i + 1) & 1)) return false; } return true; } int main () { N = read(), K = read(); int t = 0, tot = (1 << N) - 1, cnt = 0; for (int i = 0;i <= tot; ++i) if (check(i)) { cnt = 0; for (int j = 0;j < N; ++j) cnt += ((i >> j) & 1); f[t][i][cnt] = 1; } t ^= 1; for (int i = 2;i <= N; ++i) { for (int j = 0;j <= tot; ++j) { if (!check(j)) continue; cnt = 0; for (int k = 0;k < N; ++k) cnt += ((j >> k) & 1); for (int k = 0;k <= tot; ++k) { if (!check(k)) continue; if (!check2(j, k)) continue; for (int p = 0;p <= K - cnt; ++p) f[t][j][p + cnt] += f[t ^ 1][k][p]; } } t ^= 1; memset(f[t], 0, sizeof(f[t])); } ll res = 0; for (int i = 0;i <= tot; ++i) res += f[t ^ 1][i][K]; printf("%lld\n", res); return 0; } |