CSES 1130 题解
CESE 1130 Tree Matching
题意
给定包含 \(n\) 个结点的树。
匹配是一个边集,并且树上的每个点在这个边集中最多连接 \( 1\) 条边。请你求出匹配中最多有多少条边。
\(1 \le n \le 2 \times 10 ^ 5\)
思路
首先,这个题目每次选取的是一条边,那么这一条边用父亲还是儿子来表示呢?
当然是用 儿子 来表示啦!因为每个结点只有一个父亲。
所以,\(dp _ {i, 0 / 1}\) 表示第 \(i\) 个结点联想父亲的那条边是否选择时,\(i\) 的子树上最多连了多少条边。
那么,我们就分两种情况来处理。
设当前搜索到的点的编号为 \(u\),则:
\[dp _ {u, 0} = \max \{\sum _ {v \in son _ u} dp _ {v, 0}, \max _ {v \in son _ u} \{\sum _ {v' \in son _ u} dp _ {v', 0} - dp _ {v, 0} + dp _ {v, 1}\}\}
\]
\[dp _ {u, 1} = \sum _ {v \in son _ u} dp _ {v, 0}
\]
在选择的情况中,因为点 \(u\) 已经连出去了一条边,所以它和它的所有儿子的边都不能连。
如果不选择,那么就只能在儿子中选择一条边连,或者不连。
那么最后的答案是什么呢?
因为 \(1\) 是根节点,没有父亲,所以直接输出 \(dp _ {1, 0}\) 即可。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, dp[N][2];
vector<int> g[N];
void dfs(int t, int fa) {
dp[t][1] = 1;
int s = 0;
for (int i : g[t]) {
if (i != fa) {
dfs(i, t);
dp[t][1] += dp[i][0], s += dp[i][0]; // 处理选择
}
}
dp[t][0] = s;
for (int i : g[t]) {
if (i != fa) {
dp[t][0] = max(dp[t][0], s - dp[i][0] + dp[i][1]); // 处理不选择
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
g[a].push_back(b), g[b].push_back(a); // 建树
}
dfs(1, 0);
cout << dp[1][0]; // 输出答案
return 0;
}