Loading

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;
}
posted @ 2023-03-02 22:50  chengning0909  阅读(75)  评论(0编辑  收藏  举报