UVa11825 Hackers' Crackdown

原题链接:UVa11825

状压 DP

看了刘汝佳大神的解释,妙啊。

把每个点以及与它直接相连的点放入一个集合,一共有 $n$ 个集合,然后可以把一个集合内的点的状态压缩到一个数来表示。然后枚举这 $n$ 个集合的组合方式,处理每种组合对应的所覆盖的点的状态。

$f[i]$ 表示状态 $i$ 最多分出多少组,以 $i$ 的子集来转移,只有当 $i$ 的子集能覆盖所有点的时候,才能破坏一种服务。

还需要知道如何枚举状态 $i$ 的所有子集j,代码:for (int j = i; j; j = j - 1 & i)

 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
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

const int N = 20;

int n, Case;
int s[N], f[1 << N];
int state[1 << N];

void solve() {
    for (int i = 0; i < n; i++) {
        int m; scanf("%d", &m);
        s[i] = 1 << i;
        while (m--) {
            int x; scanf("%d", &x);
            s[i] |= 1 << x;
        }
    }
    for (int i = 0; i < 1 << n; i++) {
        state[i] = 0;
        for (int j = 0; j < n; j++) {
            if (i >> j & 1)
                state[i] |= s[j];
        }
    }
    for (int i = 1; i < 1 << n; i++) {
        f[i] = 0;
        for (int j = i; j; j = j - 1 & i) { // 枚举i的子集j
            if (state[j] == ((1 << n) - 1)) // 只有这个组合所表示的状态能覆盖所有点时才能转移
                f[i] = max(f[i], f[i ^ j] + 1);
        }
    }
    printf("Case %d: %d\n", ++Case, f[(1 << n) - 1]);
}

int main() {
    while (cin >> n, n) solve();
    return 0;
}
updatedupdated2020-05-112020-05-11
加载评论
点击刷新