跳转至

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;
}