201503-4 网络延时 100分 31ms

点击前往试题目录:https://blog.csdn.net/best335/article/details/99550556
在这里插入图片描述
参考:https://www.cnblogs.com/pisceskkk/p/10423535.html
引言
树作为一种特殊的图,具有很多良好的性质,树的直径便是其中之一。

定义
树的直径有许多相近的定义。但由于没有找到比较权威的定义,就用自己的语言大概表述一下吧。
对于一棵带非负边权的树,定义两点间距离为两点间路径的边权之和,树的直径就是距离最远的两点之间的路径,同时也称该距离为树的直径。
简而言之,树的直径就是树上最长的简单路径。

性质
直径两端点一定是两个叶子节点。

距离任意点最远的点一定是直径的一个端点。

对于两棵树,如果第一棵树直径两端点为(u,v),第二棵树直径两端点为(x,y),用一条边将两棵树连接,那么新树的直径一定是u,v,x,y中的两个点。

性质证明
显然成立,证明从略。

情况一:点a在直径(u,v)上。若存在点b使得dis(a,b)>dis(a,u)并且dis(a,b)>dis(a,v) ,则有 dis(b,v)或dis(b,u)>dis(u,v),即(u,v)不是该树的直径,矛盾。
情况二:点a不在直径(u,v)上。若存在点b使得dis(a,b)>dis(a,u)并且dis(a,b)>dis(a,v) ,不妨取直径的其中一个端点u分析。
分析图如下(灵魂画手上线):
图3.2.1
在这里插入图片描述
图片注:u,v 为直径端点,c为(a,b)与(a,u)交点,d为(a,u)与(v,u)交点。

由于dis(a,b)>dis(a,u) ,可知 dis(c,b)>dis(c,u),进而得到dis(d,b)>dis(d,u),最终可知dis(v,b)>dis(v,u),即(u,v)不是该树的直径,矛盾。

如果新树直径不是原来两棵树中一棵的直径,那么新直径一定经过两棵树的连接边,新直径在原来每棵树中的部分一定是距离连接点最远的点,即一定是原树直径的一个端点。

求解算法
共有两种时间复杂度为 O(N) 的算法,一种是dp,另一种是贪心算法。

方法一:动态规划
根据定义,我们只需要求出最大链长即可。
首先将该树转化为有根树。设f[i]为以节点i为根的子树的最大深度,有状态转移方程
在这里插入图片描述
那么在该子树中经过节点i的最长链长度为 f[i] 与次大 f[j] (缺省值为0)之和。在整个遍历过程中记录最大链长即可。

方法二:贪心算法
根据树的直径性质2,我们只需要任取一点s开始DFS(或BFS)即可找到距离该点的最远点,即直径端点之一,记为u。同样的,从节点u开始我们便可以找到另一个端点v,过程中记录距离即可得到树的直径。
相较于方法一,方法二可以更方便地找到直径的端点,进而得到直径的具体路径。

//按深度搜索距离端点1的最远端点S 再从端点S出发搜索(到T的)最大深度即为答案。
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int main(){
	vector<int>A[20001];
	int n,m,s,t,p,ans=-1,v[20001];
	cin>>n>>m;
	for(int i=2,a,ni=n+m+1;i<ni;++i)
		cin>>a,A[i].push_back(a),A[a].push_back(i);
	queue<int> q;
	q.push(1),q.push(-1),memset(v,0,sizeof(v));
	while(!q.empty()){
		while(q.front()!=-1){
			p=q.front(),q.pop();
			if(v[p]==0){
				v[s=p]=1;
				for(int i=0,ni=A[s].size();i<ni;++i)q.push(A[s][i]);
			}
		}
		q.pop();
		if(q.size()>0) q.push(-1);
	}
	q.push(s),q.push(-1),memset(v,0,sizeof(v));
	while(!q.empty()){
		while(q.front()!=-1){
			p=q.front(),q.pop();
			if(v[p]==0){
				v[t=p]=1;
				for(int i=0,ni=A[t].size();i<ni;++i)q.push(A[t][i]);
			}
		}
		q.pop();
		if(q.size()>0)q.push(-1),++ans;
	}
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值