旅游规划

旅游规划

W 市的交通规划出现了重大问题,市政府下定决心在全市各大交通路口安排疏导员来疏导密集的车流。

但由于人员不足,W 市市长决定只在最需要安排人员的路口安排人员。

具体来说,W 市的交通网络十分简单,由 n 个交叉路口和 n1 条街道构成,交叉路口路口编号依次为 0,1,,n1

任意一条街道连接两个交叉路口,且任意两个交叉路口间都存在一条路径互相连接。

经过长期调查,结果显示,如果一个交叉路口位于 W 市交通网最长路径上,那么这个路口必定拥挤不堪。

所谓最长路径,定义为某条路径 p=(v1,v2,,vk),路径经过的路口各不相同,且城市中不存在长度大于 k 的路径(因此最长路径可能不唯一)。

因此 W 市市长想知道哪些路口位于城市交通网的最长路径上。

输入格式

第一行包含一个整数 n

之后 n1 行每行两个整数 u,v,表示编号为 uv 的路口间存在着一条街道。

输出格式

输出包括若干行,每行包括一个整数——某个位于最长路径上的路口编号。

为了确保解唯一,请将所有最长路径上的路口编号按编号顺序由小到大依次输出。

数据范围

1n2×105

输入样例:

复制代码
10
0 1
0 2
0 4
0 6
0 7
1 3
2 5
4 8
6 9
复制代码

输出样例:

复制代码
0
1
2
3
4
5
6
8
9
复制代码

 

解题思路

  题意就是求树的直径上的所有点,其中可能存在多条树的直径。

  这里用树形dp来求树的直径。

  对于一颗树,当我们选定一个点为根后,那么对于任何一条路径,都存在一个唯一的最高点。即不管是哪条路径,其最高点是唯一的。如图:

  直径是所有路径里面的最大值。将所有路径按照最高点进行分类,划分集合,则最高点为1的是一类,最高点为2的是一类,......,以此类推,一共可以分成n组,树的直径就是某一组中的路径的最大值。

  记每个以i为最高点,且以i为端点的所有路径的最大值为d1[i],次大值为d2[i]

  对于根节点,先求出所有子节点的最大路径(即以子节点端点的最长的路径长度,同时子节点也是这条路径的最高点)d1[i]。然后从这些d1[i]中,找到最大值和次大值,从而得到以根节点为最高点的,且以根节点为端点的所有路径的最大值d1[root]和次大值d2[root]

  接下来我们从根节点出发,选择两个以子节点为端点的路径与根节点连成一条更长的路径,此时根节点仍为路径的最高点,但路径不是以根节点为端点了。要使得路径长度最大,就是d1[root]+d2[root]。同时这条最长路径,对应以根节点为最高点的那个集合的最大值。

  因此要求树的直径,就是求以每一个结点为根节点的d1[root]+d2[root],树的直径就是这些值中的最大值。

  求最大路径的题可以看这篇博文:大臣的旅费:https://www.cnblogs.com/onlyblues/p/15940402.html

  但这题并不是求树的直径,而是树的直径上的所有点。方法是遍历每一个点,看一下这个点是否在树的直径上。即选择两个与该结点相邻的结点(叶子结点除外),将以这两个结点为端点的路径与该结点相连,与上面的做法相同,判断这条连接的路径是否为树的直径就可以了。

  对于树中任何一个结点,都有一个向上的结点(这个结点的父节点)和向下的若干个结点(子节点。叶子结点除外)。对于向下的子节点,前面我们已经求得最大路径长度d1[root]和次大路径长度d2[root]。对于向上的父结点,这里记向上的最大路径长度为up[root],表示以根节点为端点(不是最高点)的一条向上的路径,这条路径长度的取值又取决于其父节点的up[fa],并且要取尽可能大的值。我们就是要找到d1[root]d2[root]up[root]这三个的最大值和次大值,两者相加,如果长度等于树的直径,那么这个点就在树的直径上,否则不在。

  从根节点往上走,走的到父节点,再从父节点走的话又可以走多远。这个最远的值加上1就是up[root]。从根节点的父节点fa走有两种值:

  • fa往上继续走,up[fa]
  • fa往下走:
    • 如果根节点rootd1[fa]这条路径上,那么不能用这条路径。由于要取尽可能大的值,因此这种情况要选择d2[fa]
    • 如果根节点root不在d1[fa]这条路径上,那么就取d1[fa]

  最后up[root]就是这两种情况的最大值再加上1。本质就是找一条从父节点出发的最长路径,这条路径不能够经过子节点且要是最长的。

  为了判断根节点root是否在d1[fa]这条路径上,这里还需要开一个数组来记录每个结点的最长路径d1[root]所对应的那个子节点。

  求d1[root]d2[root]是一个自下而上的过程,而求up[root]则是一个自上而下的过程。

  AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 const int N = 2e5 + 10, M = N << 1;
 7 
 8 int head[N], e[M], ne[M], idx;
 9 int d1[N], d2[N], son[N], up[N];
10 int maxd;
11 
12 void add(int v, int w) {
13     e[idx] = w, ne[idx] = head[v], head[v] = idx++;
14 }
15 
16 void dfs_d(int src, int pre) {
17     for (int i = head[src]; i != -1; i = ne[i]) {
18         if (e[i] != pre) {
19             dfs_d(e[i], src);   // 求子节点的最大路径长度
20             
21             // 更新根节点的最大路径长度和次大路径长度
22             int d = d1[e[i]] + 1;
23             if (d >= d1[src]) {
24                 d2[src] = d1[src];
25                 d1[src] = d;
26                 son[src] = e[i];
27             }
28             else if (d > d2[src]) {
29                 d2[src] = d;
30             }
31         }
32     }
33     
34     maxd = max(maxd, d1[src] + d2[src]);    // 树的直径就是每个结点的d1+d2,取最大值
35 }
36 
37 void dfs_up(int src, int pre) {
38     for (int i = head[src]; i != -1; i = ne[i]) {
39         if (e[i] != pre) {
40             // 自下而上的过程,当枚举都某个结点时,这个结点的up值已经确定,则其子节点的up值也就确定了
41             if (son[src] == e[i]) up[e[i]] = max(up[src], d2[src]) + 1; // 如果子节点在src的d1这条路径上,那么不可以取d1[src]
42             else up[e[i]] = max(up[src], d1[src]) + 1;  // 如果子节点不在src的d1这条路径上,可以取d1[src]
43             dfs_up(e[i], src);
44         }
45     }
46 }
47 
48 int main() {
49     memset(head, -1, sizeof(head));
50     
51     int n;
52     scanf("%d", &n);
53     for (int i = 0; i < n - 1; i++) {
54         int v, w;
55         scanf("%d %d", &v, &w);
56         add(v, w), add(w, v);
57     }
58     
59     dfs_d(0, -1);
60     dfs_up(0, -1);
61     
62     for (int i = 0; i < n; i++) {
63         int d[3] = {d1[i], d2[i], up[i]};
64         sort(d, d + 3);
65         if (d[1] + d[2] == maxd) printf("%d\n", i); // 取每个结点的d1,d2,up中的最大的两个连成一条路径,再判断是不是树的直径长度
66     }
67     
68     return 0;
69 }
复制代码

 

参考资料

  AcWing 1078. 旅游规划(蓝桥杯C++ AB组辅导课):https://www.acwing.com/video/792/

posted @   onlyblues  阅读(162)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2021-03-13 图解带头节点的单链表的反转操作
Web Analytics
点击右上角即可分享
微信分享提示