1078. 旅游规划 树形dp 图论

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

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

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

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

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

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

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

输入格式
第一行包含一个整数 n。

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

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

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

数据范围
1≤n≤2×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

思路
这本身是一个无根树,可以对每个节点都作为根去求,但是这样会超时

任取一个节点作为根,由直径的性质可以知道,直径中必然有一点是最高点,不管这点是不是根节点,因此我们第一遍(自底向上)dfs时求出当前节点向下的两个最大路径时取一下max就可以得到直径的长度,因为总能到达那个最高点,它的两个最大路径都是向下的。

由于需要求出直径上的说所有点,那么就根据:最大的两个路径长度之和等于maxd来判断节点是否在直径上,但是因为我们第一遍dfs都是向下的,除了根节点,其它节点都还要判断向上的情况,因此需要一遍自顶向下的dfs,求出每个节点的向上最大路径值,这也是dp的过程,递推关系:up[j]要么等于上一个节点的d1(j不在u的d1路径上),要么等于d2(j在u的d1路径上)。
和树的重心那题的区别:

树的重心:可以一遍dfs求出向下的节点,同时根据n-sum(子节点),得出向上的值
这题: 一遍dfs只能求出向下的情况,对向上的情况只能通过再用一个dfs用递推来求
代码
import java.util.*;

public class Main{
	static int N=200005,M=N*2,n;
	static int idx,h[]=new int [N],ne[]=new int [M],e[]=new int [M];
	static int d1[]=new int [N],d2[]=new int [N],up[]=new int [N],p1[]=new int [N];
	static int maxd=0;
	static void add(int a,int b){
		e[idx]=b;ne[idx]=h[a];h[a]=idx++;
	}
	static void dfs_d(int u,int f){
		for(int i=h[u];i!=-1;i=ne[i]){
			int j=e[i];
			if(j!=f){
				dfs_d(j,u);
				int d=d1[j]+1;
				if(d>d1[u]){
					d2[u]=d1[u];
					d1[u]=d;
					p1[u]=j;
				}else if(d>d2[u]){
					d2[u]=d;
				}
			}
		}
		maxd=Math.max(maxd, d1[u]+d2[u]);
	}
	
	static void dfs_up(int u,int f){
		for(int i=h[u];i!=-1;i=ne[i]){
			int j=e[i];
			if(j!=f){
				up[j]=up[u]+1;
				if(p1[u]==j)up[j]=Math.max(up[j], d2[u]+1);
				else up[j]=Math.max(up[j],d1[u]+1);
				dfs_up(j,u);
			}
		}
	}
	
    public static void main(String []args){
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        Arrays.fill(h, -1);
        for(int i=0;i<n-1;++i){
        	int a=sc.nextInt();
        	int b=sc.nextInt();
        	add(a,b);add(b,a);
        }
        dfs_d(0,-1);
        dfs_up(0,-1);
        
        for(int i=0;i<n;++i){
        	int t[]={d1[i],d2[i],up[i]};
        	Arrays.sort(t,0,3);
        	if(t[1]+t[2]==maxd){
        		System.out.println(i);
        	}
        }
    }
    
    
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wow_awsl_qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值