首页 > 其他分享 >树的直径、重心、中心

树的直径、重心、中心

时间:2023-10-20 10:37:07浏览次数:39  
标签:sz 重心 中心 int max dfs 直径 d2 d1

树的直径、重心、中心

一、树的直径

我们将一棵树 \(T=(V,E)\) 的直径定义为 $max(u,v)(u,v∈V) $,即树中所有最短路径距离的最大值即为树的直径。

求法:

1)树形dp

定义d1为从节点u到其子树中节点距离的最大值,d2为次大值,则\(diameter=max(d1+d2)\)

特点:不可输出路径,但可以处理负边权的树

inline int dfs(int u,int f){
	int d1=0,d2=0,d;
	for(auto k:g[u]){
		int v=k.first,w=k.second;
		if(v==f) continue;
		d=dfs(v,u)+w;
		if(d>d1) d2=d1,d1=d;
		else if(d>d2) d2=d;
	}
	ans=max(ans,d1+d2);
	return d1;
}

另一种写法(感觉比较少见)

定义dp[u]为以u到其子树内节点的最远距离,则待选的一条直径可以表示为两条这样的链长之和(其实就是次长链和长链),在用本次的dp[v]去更新dp[u]之前,二者一定是两条不同的链,可以很方便地更新答案

inline void dfs(int u,int f){
	for(int i=first[u];i;i=e[i].ne){
		int v=e[i].v,w=e[i].w; if(v==f) continue;
		dfs(v,u);
		ans=max(ans,d[u]+d[v]+w);//ans即为所求
		d[u]=max(d[u],d[v]+w);
	}
}

2)两次dfs

第1次dfs从任意点出发,到达的离它最远点一定是树的直径的一个端点(具体证明参见),将其记为p

第2次dfs从点p出发,到达的最远点一定是树的直径的另一个端点,所以记录d[i]为从起点到i的路径长度,则最终的d[p]即为树的直径的长度

特点:记录每个点的前驱pre[i]后可以输出树的一条直径上的所有点,但不能处理负边权的树(参见证明)

inline void dfs(int u,int f){
	pre[u]=f;
	if(d[u]>d[p]) p=u;
	for(int i=first[u];i;i=e[i].ne){
		int v=e[i].v,w=e[i].w;
		if(v==f) continue;
		d[v]=d[u]+w;
		dfs(v,u);
	}
}
int main(){
	int u,v,w;
	scanf("%d",&n); F(i,1,n-1){
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w); add(v,u,w);
	}
	dfs(1,0); d[p]=0;
	dfs(p,0); int x=p; while(pre[x]) printf("%d->",x),x=pre[x]; printf("%d\n",x);//直径具体路径 
	printf("%d",d[p]);//直径长度 
	return 0;
}

二、树的重心

在一棵树上,若删去节点u后得到的最大连通块(又叫最大独立集)的点数最小,则u是这棵树的重心

性质1:一棵树最多有两个重心

性质2:去掉重心后最大独立集大小不超过n/2

性质3:重心到树上各点的距离和最小,即能使\(\sum_{i∈V} dis(i,u)\)最小化

用树形DP求解,时间复杂度\(O(N)\)

inline void dfs(int u,int f){
	sz[u]=1; int maxn=0;
	for(int i=first[u];i;i=e[i].ne){
		int v=e[i].v; if(v==f) continue;
		dfs(v,u);
		sz[u]+=sz[v];
		if(sz[v]>maxn) maxn=sz[v];
	}
	maxn=max(maxn,n-sz[u]);
	if(maxn<mn) ans[cnt=1]=u,mn=maxn;
	else if(maxn==mn) ans[++cnt]=u;
}

另一种写法,用性质2

inline void dfs(int u,int f){
	bool chk=1; int mx=-1;
	sz[u]=1; for(int i=first[u];i;i=e[i].ne ){
		int v=e[i].v; if(v==f) continue;
		dfs(v,u); sz[u]+=sz[v];  mx=max(sz[v],mx);
		if(sz[v]>n/2) chk=0;
	}
	if(n-sz[u]>n/2) chk=0;
	if(chk) zx[++cnt]=u,ans=max(mx,n-sz[u]);
}

应用1:洛谷P1395 会议

求图上一点u,使\(\sum_{i∈V} dis(i,u)\)最小化

解法1:用性质3,当断掉u后树的形态尽量均衡(或者说平衡)时结果一定最小,即求树的重心,然后在以以之为根dfs一遍求出dis即可

解法2:树形DP,记\(dp_u\)表示在u点开会的距离之和,则有转移方程:

\[dp_u=dp_{fa}+(n-sz_u)-sz_u=dp_{fa}+n-2*sz_u \]

最后对dp取min即可

三、树的中心

到树上各点的最大距离最小,即\(max_{i∈V}dis(i,u)\)最小的节点是树的中心

性质1:树的中心一定在树的直径上

性质2:一棵树最多有2个中心

求法:

1)延续树的直径的dp做法,记录d1,d2,up分别表示向下走的最大值,次大值,向上走的最大值。

对于d1,d2:同树的直径

对于up:如果v在u的向下最大链上,那就用d2[u]更新up[v],否则用d1[u]

inline void dfsdown(int u,int f){	
	int d;
	for(int i=first[u];i;i=e[i].ne){
		int v=e[i].v,w=e[i].w; if(v==f) continue;
		dfsdown(v,u);
		d=d1[v]+w;
		if(d>d1[u]) d2[u]=d1[u],d1[u]=d,p[u]=v;//记录v在u的最长链上 
		else d2[u]=d;
	}
}
inline void dfsup(int u,int f){
	for(int i=first[u];i;i=e[i].ne){
		int v=e[i].v,w=e[i].w; if(v==f) continue;
		if(p[u]==v) up[v]=max(up[u],d2[u])+w;
		else up[v]=max(up[u],d1[u])+w;
		dfsup(v,u);
	}
}
int main(){
	scanf("%d",&n); int u,v,w;
//	mem(d1),mem(d2),mem(up);
	F(i,1,n-1){
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w); add(v,u,w);
	}
	dfsdown(1,0);
	dfsup(1,0);
	F(i,1,n){
		int z=max(d1[i],up[i]);//z即节点i与其他节点的最远距离 
		if(z<dis) zx[cnt=1]=i,dis=z;//最小化最远距离的点即为树的中心 
		else if(z==dis) zx[++cnt]=i;
	}
	printf("%d %d\n",cnt,dis); 
	F(i,1,cnt) printf("%d ",zx[i]);
	return 0;
}

2)用性质1,先找出直径,然后统计出直径上每一点到两端点的距离最后取min即为树的中点

inline void dfs(int u,int f){
	pre[u]=f;
	if(d[u]>d[p]) p=u;
	for(int i=first[u];i;i=e[i].ne){
		int v=e[i].v,w=e[i].w;
		if(v==f) continue;
		d[v]=d[u]+w;
		dfs(v,u);
	}
}
inline void dfs2(int u,int f){
//	if(u!=st) printf("%d=>",u);//输出直径 
	int z=max(dis,d[u]); 
	if(z<mn) mn=z,zx[cnt=1]=u;
	else if(z==mn) zx[++cnt]=u;
	for(int i=first[u];i;i=e[i].ne){
		int v=e[i].v,w=e[i].w; if(v==f || v!=pre[u]) continue;
		dis+=w; dfs2(v,u);
	}
}
int main(){
	int u,v,w;
	scanf("%d",&n); F(i,1,n-1){
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w); add(v,u,w);
	}
	dfs(1,0); d[p]=0;
	dfs(p,0); st=p;
	dfs2(p,0); 
//	printf("%d\n",st);//输出直径 
	printf("%d %d\n",cnt,mn);
	F(i,1,cnt) printf("%d ",zx[i]);
	return 0;
}

标签:sz,重心,中心,int,max,dfs,直径,d2,d1
From: https://www.cnblogs.com/superl61/p/17776445.html

相关文章

  • 态路小课堂丨400G QSFP112—助力IDC数据中心升级
    TARLUZ态路来源网络随着IDC数据中心不断的发展,光模块向着更高速率、更小的尺寸和更低损耗不断升级,以适应不同使用场景。光模块一般采用提高单通道比特速率、增加通道数或改变调制方式来实现光模块的速率升级。如上图所示,400G光模块有56GPAM4和112GPAM4两种调制方案,本文态路为您介......
  • 智安网络|从区块链到社交网络:解析去中心化的意义与应用
    在当今数字化的世界中,一个越来越常见的概念是“去中心化”。从区块链技术到金融系统,从社交网络到数据存储,去中心化被认为是一种前所未有的方式来重新定义和改变传统的中心化结构。那么,去中心化到底是什么?首先,去中心化是一种思想和哲学,旨在消除中心化权力和控制。传统的中心化结构通......
  • 对话在行人|九州通:携手用友打造招聘共享中心实现招聘数智化
    对话在行人从信息化在行人到数智化在行人,用友持续深耕企业软件与服务产业35年,截至目前已有3.96万家大中型企业选择用友BIP推进数智商业创新。为探索行业数智化成功路径,分享企业数智化领先实践,2023年9月,用友正式推出聚焦行业领先企业数智化转型的高端访谈栏目《对话在行人》。此栏目......
  • Nacos注册中心
     服务注册到Nacos1.在cloud-demo父工程中添加spring-cloud-alilbaba的管理依赖:<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.6.RELEASE</version>......
  • Eureka注册中心
    Eureka的作用 消费者该如何获取服务提供者具体信息?服务提供者启动时向eureka注册自己的信息eureka保存这些信息消费者根据服务名称向eureka拉取提供者信息如果有多个服务提供者,消费者该如何选择?服务消费者利用负载均衡算法,从服务列表中挑选一个消费者如何感知......
  • DevOps2023现状报告|注重文化、以用户为中心是成功的关键
    GoogleCloudDORA团队的一份新研究报告强调了企业文化和关注用户作为成功软件交付支柱的重要性。 2023DevOps状况报告分析了过去9年来通过此类最大规模调查收集的全球36,000多名IT专业人员的数据。今年的报告是继2022年调查之后发布的,该调查发现越来越多的人采用工......
  • 金融液冷数据中心,噱头还是趋势?
    当前,全社会数字化进程加速,金融行业已全面进入数字化和智能化时代。与此同时,随着云计算、分布式架构、大数据分析、通用人工智能等技术的广泛运用,金融行业从数据大集中到分布式融合,金融企业的数据中心建设正围绕其业务发展和转型需要迅速展开。在此过程中,金融数据中心在节能减排领域......
  • [CSP-S2019] 树的重心 题解
    [CSP-S2019]树的重心因为这道题令我十分兴奋,所以来写一下做完后的思考。这道题用到了树的重心的种种性质,在写解法的时候会一一点出其用处。首先,枚举每一条边,然后各自\(O(n)\)扫一次的\(O(n^2)\)做法是简单的。那么接下来,就会出现不同的解法了:优化\(O(n)\)求解的过程......
  • FlashDuty Changelog 2023-09-21 | 自定义字段和开发者中心
    FlashDuty:一站式告警响应平台,前往此地址免费体验!自定义字段FlashDuty已支持接入大部分常见的告警系统,我们将推送内容中的大部分信息放到了Lables进行展示。尽管如此,我们用户还是会有一些扩展或定制性的需求,比如人工标记一个故障是否为误报。因此我们提供了自定义字段功能,......
  • 数据中心布线数字孪生管理技术
     以往,数据中心布线管理模式=表格+图纸第一步:进行现场变更实施;第二步:为了后续的变更,需要准确了解已经做了什么,我们用EXCEL或CAD图纸记录变更文档。这种方式看似正确,然而,随着数据中心规模不断扩大,光纤数量多达数十万芯,管理难度变大,这种顺序方法不再有效:无法在实施变更前模拟校验变......