#10159. 「一本通 5.2 练习 2」旅游规划
题意
由于有 个交通路口, 条街道, 所以我们可以断定, 这是一颗树.
又由于让我们求出这棵树的最长路径, 很简单就能想到树形dp (貌似两遍 dfs 更简单?)
简化一下: 给定一棵树, 求出他的 所有 直径. (注意"所有", 这是题目的难点.)
案例
以 为根观察这棵树:
显然这棵树有 条直径, 长度为 , 分别是
3-1-0-2-5
3-1-0-4-8
3-1-0-6-9
5-2-0-4-8
5-2-0-6-9
8-4-0-6-9
可以发现 中, 只有 不处于任意一条直径上, 故输出中没有 .
直径
要想做这题, 首先要会求树的直径的长度, 不会可以看这篇:如何求树的直径
路径
首先需要用 dfs1
求出直径的长度 length
.
假设 表示以 为根的子树中最长的简单路径的长度, 是以 为根的子树中次长的简单路径的长度.
显然, 当点 满足 时他为某一条直径上的点.这个很好找, 只需要声明一个 dfs2
遍历即可.
再从这个点出发, 寻找他的子节点, 如果子节点 满足 或 ,则说明使用了 更新 了, 即 也在直径上.可以用 dfs3
寻找 , 不过注意要维护 或 两种情况.
代码
#include <bits/stdc++.h>
using namespace std;
inline int read() {//快读
int x = 0; char c = getchar(); bool f = 1;
while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return (f ? x : -x);
}
const int N = 1e6 + 10;//数组习惯开大一点
int n, res, dp[N][2];//节点数, 直径长度, dp[i][0]以i为子树中的最长路径(直径), 1为次长路径
bool vis[N];//标记是否在直径上
int cnt = -1, head[N];//链式前向星
struct edge {
int to, nxt;
} e[N << 1];
inline void add(int u, int v) {//加边
e[++cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
inline void adde(int v, int u) { add(u, v); add(v, u); }
inline void dfs3(int, int, int) ;//由于dfs2在dfs3前面定义, 所以需要提前声明一下
inline void dfs(int u, int fa) {//寻找直径长度
for(int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
dfs(v, u);
if(dp[u][0] < dp[v][0] + 1) { dp[u][1] = dp[u][0]; dp[u][0] = dp[v][0] + 1; }
else if(dp[u][1] < dp[v][0] + 1) dp[u][1] = dp[v][0] + 1;
res = max(res, dp[u][0] + dp[u][1]);
}
}
inline void dfs2(int u, int fa) {//寻找u
if(dp[u][0] + dp[u][1] == res) {
dfs3(u, fa, 0);
dfs3(u, fa, 1);
}
for(int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
dfs2(v, u);
}
}
inline void dfs3(int u, int fa, int p) {//寻找v
vis[u] = 1;
for(int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
if(dp[u][p] == dp[v][0] + 1) dfs3(v, u, 0);
}
}
int main() {
n = read();
for(int i = 1; i <= n; ++i) head[i] = -1;
for(int i = 1; i < n; ++i) adde(read() + 1, read() + 1);
dfs(1, 0);
dfs2(1, 0);
for(int i = 1; i <= n; ++i)//输出
if(vis[i]) printf("%d\n", i - 1);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】