1 条题解
-
019220448 董文杰 (董文杰) LV 9 @ 2023-11-05 23:30:20
算法:换根dp | BFS
题意:给定一棵树,现在需要选择其中的一个结点为根节点,使得深度和最大。深度的定义是以每个结点到树根所经历的结点数
思路:
暴力:
- 显然可以直接遍历每一个结点,计算每个结点的深度和,然后取最大值即可
- 时间复杂度:\(O(n^2)\)
优化:
先看图:
我们可以发现,对于当前的根结点
fa
,我们选择其中的一个子结点ch
,将ch
作为新的根结点(如右图)。那么对于当前的ch
的深度和,我们可以借助fa
的深度和进行求解。我们假设以ch
为子树的结点总数为x
,那么这x
个结点在换根之后,相对于ch
的深度和,贡献了-x
的深度;而对于fa
的剩下来的n-x
个结点,相对于ch
的深度和,贡献了n-x
的深度。于是ch
的深度和就是fa的深度和
-x+n-x
,即
dep[ch] = dep[fa]-x+n-x = dep[fa]+n-2*x
于是我们很快就能想到利用前后层的递推关系,\(O(1)\) 的计算出所有子结点的深度和代码实现:我们可以先计算出
base
的情况,即任选一个结点作为根结点,然后基于此进行迭代计算。在迭代计算的时候需要注意的点就是在一遍dfs
计算某个结点的深度和dep[root]
时,如果希望同时计算出每一个结点作为子树时,子树的结点数,显然需要分治计算一波。关于分治的计算我熟练度不够高,~~特此标注一下debug了3h的点~~:即在递归到最底层,进行回溯计算的时候,需要注意不能统计父结点的结点值(因为建的是双向图,所以一定会有从父结点回溯的情况),那么为了避开这个点,就需要在 \(O(1)\) 的时间复杂度内获得当前结点的父结点的编号,从而进行特判,采用的方式就是增加递归参数fa
。没有考虑从父结点回溯的情况的dfs代码
void dfs(int now, int depth) { if (!st[now]) { st[now] = true; dep[root] += depth; for (auto& ch: G[now]) { dfs(ch, depth + 1); cnt[now] += cnt[ch]; } } }
考虑了从父结点回溯的情况的dfs代码
void dfs(int now, int fa, int depth) { if (!st[now]) { st[now] = true; dep[root] += depth; for (auto& ch: G[now]) { dfs(ch, now, depth + 1); if (ch != fa) { cnt[now] += cnt[ch]; } } } }
时间复杂度:\(O(2n)\)
暴力代码:
const int N = 500010; int n; vector<int> G[N]; int st[N], dep[N]; void dfs(int id, int now, int depth) { if (!st[now]) { st[now] = 1; dep[id] += depth; for (auto& node: G[now]) { dfs(id, node, depth + 1); } } } void solve() { cin >> n; for (int i = 1; i <= n - 1; i++) { int a, b; cin >> a >> b; G[a].push_back(b); G[b].push_back(a); } int res = 0; for (int i = 1; i <= n; i++) { memset(st, 0, sizeof st); dfs(i, i, 1); res = max(res, dep[i]); } cout << res << "\n"; }
优化代码:
const int N = 500010; int n, dep[N], root = 1; vector<int> G[N], cnt(N, 1); bool st[N]; // 当前结点编号 now,当前结点的父结点 fa,当前结点深度 depth void dfs(int now, int fa, int depth) { if (!st[now]) { st[now] = true; dep[root] += depth; for (auto& ch: G[now]) { dfs(ch, now, depth + 1); if (ch != fa) { cnt[now] += cnt[ch]; } } } } void bfs() { memset(st, 0, sizeof st); queue<int> q; q.push(root); st[root] = true; while (q.size()) { int fa = q.front(); // 父结点编号 fa q.pop(); for (auto& ch: G[fa]) { if (!st[ch]) { st[ch] = true; dep[ch] = dep[fa] + n - 2 * cnt[ch]; q.push(ch); } } } } void solve() { cin >> n; for (int i = 1; i <= n - 1; i++) { int a, b; cin >> a >> b; G[a].push_back(b); G[b].push_back(a); } dfs(root, -1, 1); bfs(); cout << *max_element(dep, dep + n + 1) << "\n"; }
- 1
信息
- ID
- 1534
- 难度
- 7
- 分类
- (无)
- 标签
- (无)
- 递交数
- 25
- 已通过
- 6
- 通过率
- 24%
- 上传者