P2016 战略游戏 题解

这题一眼看上去是一道树形DP的题目,然后后面一想好像可以用二分图来做(虽然是教练提醒的),故产生了此份题解。

题目大意

给定一个无根树,每个节点可以“管制”一堆给定点,求选择最少的点数来覆盖这棵树。

Solution

这题的“覆盖”看起来有那么亿点点像最小点覆盖,所以可以想想如何用二分图来解。

如何建立二分图?

注意到这是一棵树,则每层之间肯定不会有连接,而且没有跨越两层的边,所以我们可以把原图划分为奇数层和偶数层。

然后跑匈牙利貌似就可以了。

Tips:最小点覆盖数就是最大匹配数。

Code

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
#include <bits/stdc++.h>
using namespace std;

const int N = 1505;

vector <int> G[N];

int n, ind, k, r;
int ans, tot, dep[N], points[N];//tot表示奇数层点个数,dep[]表示每个点的层数,points[]表示所有奇数层点

bool vis[N];

void Pre (int k) {//处理出层数
for (int i = 0; i < (int)G[k].size(); ++i) {
int to = G[k][i];
if (vis[to]) continue ;
vis[to] = true, dep[to] = dep[k] + 1;
Pre(to);
}
}

int match[N];

bool DFS (int k) {//匈牙利板子
for (int i = 0; i < (int)G[k].size(); ++i) {
int to = G[k][i];
if (!vis[to]) {
vis[to] = true;
if (!match[to] || DFS(match[to])) {
match[to] = k; return true;
}
}
}
return false;
}

int main() {
scanf("%d", &n);
for (int Case = 0; Case ^ n; ++Case) {
scanf("%d%d", &ind, &k); ++ind;
for (int i = 0; i ^ k; ++i) {
scanf("%d", &r); ++r;
G[ind].push_back(r), G[r].push_back(ind);
}
}
vis[1] = true, dep[1] = 1, Pre(1);
for (int i = 1; i <= n; ++i)//取出奇数层点
if (dep[i] % 2 == 1) points[++tot] = i;
for (int i = 1; i <= tot; ++i) {//最大匹配
memset(vis, false, sizeof(vis));
if (DFS(points[i])) ++ans;
}
printf("%d\n", ans);
return 0;
}

P2016 战略游戏 题解
https://sobaliuziao.github.io/2021/04/02/post/d35df0b8.html
作者
Egg_laying_master
发布于
2021年4月2日
许可协议