P7238「DCOI」迷失森林 题解

· · 题解

前言

第一眼看这道题时,还以为是 DFS。(太蒻了)

结果看了标签,树形 DP 好题。

看到这里,有些人又会说,树形 DP 是啥,咋写?

如果对树形 DP 不太熟悉的同学,可以先去洛谷做一些题目,下面推荐一些经典的好题:

经典好题,这里不再赘述了,想学的先 AC 这道题。

极其良心变态的一道题目,在这里表扬出题人。

名字长得像拓扑排序,但是个 DP。

这道题一看就知道是树的直径问题,只不过树变成了森林。

解法

有些人可能不知道树的直径问题,树的直径指的是树中的最远的两点间距离。

一看这个题面,可能有人认为这个题很难,什么“单位树”啊、“简单路径”啊、看起来对一些刚学图论的萌新很不友好。

但是,这题并没有很难。

\Large{f_i} 为以第 i 棵单位树的的第 1 个节点,也就是题目描述中的二元组 (i, 1),为连接操作的链的一端所获得的最大直径。

显而易见,\Large{f_i} 的值从两个来源贡献而来。

第一个是单位树的最大直径,即为代码中的 maxdepth,这个很容易预处理得来。(见我代码中的深搜)

第二个是单位树的儿子,这里叫 son,还有自己到根节点的深度 depth_i

$$ f_i = \max\{maxdepth, f_{son} + depth_i\} $$ 这里再给出树形 DP 的一般形态。 ``` cpp void dfs(int u, int fa) { f[u] = __start_value; for(int i = head[u]; i; i = edge[i].nxt) { int v = edge[i].to; if(fa == v) //判重 continue; dfs(v, u); //下一层 f[u] = max(f[u], f[v] + w); } } ``` 言归正传,这题到这里还没有结束,$f_i$ 并不是我们要的答案。 这个时候维护两个值,最大子树直径与次大子树直径,即为代码中的 $max1$ 与 $max2$。 将这两个值相加并加上 $1$(因为要算上自己),取最值即可。 方程如下: $$ans = \max\{max1 + max2 + 1\}$$ 至于放在哪里,在 DP 的时候顺带维护一下即可。 至此,这道题到现在总算完结了,是不是难度还好呢? # 代码 ~~你们最想要的..~~ Talk is$\color{white}\text{n't}$ cheap, $\color{white}\text{Don't}$ show me the code... 没写链式前向星,看题解区大佬们都没写,也就没写... ``` cpp #include <bits/stdc++.h> using namespace std; int N; vector<int> edge[1000005]; long long f[1000005], ans; int depth[1000005]; int maxdepth; void get_depth(int u, int v); void dfs(int u, int v); int main() { cin >> N; for(int i = 1; i < N; i ++) { int x, y; cin >> x >> y; edge[y].push_back(x); edge[x].push_back(y); } get_depth(1, 1); dfs(1, 1); ans = max(ans, f[1] + maxdepth - 1); cout << ans << endl; return 0; } void get_depth(int u, int v) { depth[v] = depth[u] + 1; maxdepth = max(maxdepth, depth[v]); for(int i = 0; i < edge[v].size(); i ++) { int to = edge[v][i]; if(u == to) //判重 continue; get_depth(v, to); } } void dfs(int u, int v) { f[v] = maxdepth; long long max1 = -1, max2 = -1; for(int i = 0; i < edge[v].size(); i ++) { int to = edge[v][i]; if(u == to) //判重 continue; dfs(v, to); if(f[to] > max1) { max2 = max1; max1 = f[to]; } else if(f[to] > max2) { max2 = f[to]; } f[v] = max(f[v], f[to] + depth[v]); } ans = max(ans, max1 + max2 + 1); } ``` # 后言 阅读完这篇题解,你可能对于树状 DP 有了初步的了解,后面的 DP 将会越来越复杂,不仅复杂在状态的转移,更在优化的繁琐。 在这里,本人衷心祝愿每一位学习 OI 的人都不要因为一时的困难而放弃,越来越强!