#10159. 「一本通 5.2 练习 2」旅游规划

原题传送门

题意

由于有 n 个交通路口, n1 条街道, 所以我们可以断定, 这是一颗树.
又由于让我们求出这棵树的最长路径, 很简单就能想到树形dp (貌似两遍 dfs 更简单?)

简化一下: 给定一棵树, 求出他的 所有 直径. (注意"所有", 这是题目的难点.)

案例

0 为根观察这棵树:
image
显然这棵树有 6 条直径, 长度为 5 , 分别是
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

可以发现 09 中, 只有 7 不处于任意一条直径上, 故输出中没有 7.

直径

要想做这题, 首先要会求树的直径的长度, 不会可以看这篇:如何求树的直径

路径

首先需要用 dfs1 求出直径的长度 length.
假设 dp[u][0] 表示以 u 为根的子树中最长的简单路径的长度, dp[u][0] 是以 u 为根的子树中次长的简单路径的长度.
显然, 当点 u 满足 dp[u][0]+dp[u][1]=length 时他为某一条直径上的点.这个很好找, 只需要声明一个 dfs2 遍历即可.
再从这个点出发, 寻找他的子节点, 如果子节点 v 满足 dp[u][0]=dp[v][0]+1dp[u][1]=dp[v][0]+1 ,则说明使用了 dp[v] 更新 dp[u] 了, 即 v 也在直径上.可以用 dfs3 寻找 v, 不过注意要维护 dp[u][0]=dp[v][0]+1dp[u][1]=dp[v][0]+1 两种情况.

代码

#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;
}
posted @   聂天泽  阅读(89)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示