C++树形结构(3 树的中心、重心)

news2024/9/25 0:41:23

目录

一.树的中心:

1.树的概念:

2.树的性质:

性质1:

性质2:

3.树的中心求解:

4.例题:

二.树的重心:

1.基础概念:

2.求解方法:

3.例题:

4.重心的性质:

性质1:

性质2:

性质3:

性质4:

性质5:

5.例题:

①子树的重心

②唯一的重心

③会议


一.树的中心:

除了直径的端点还有一个点我们完成题目时经常会用到,这就是树的中心。

1.树的概念:

以树的中心为根时,从该根到每个叶子节点的最长路径最短,使得路径和平衡。实际应用:在若干村庄中(树形结构)修一个小学,使得所有村庄到学校的最大距离最小,小学应该修在什么位置?

2.树的性质:

性质1:

树的中心一定在直径上,且趋向于中点位置

性质2:

树的中心可以有一个(单中心),也可以有两个(双中心)

证明:引理性质2,若树的中心p不在直径st上,st上有一点q与直径联通。中心点能到的最远距离为:max(qs,qt)+pq,若要使得该值最小,pq应当为0,因此p在直径上。同时为了让max(qs,qt)更小,树的中心要在直径中点处。

3.树的中心求解:

我们现在已经知道求解任意一点到两端点的距离,即根据性质2可很轻松得到每个点能到的最长路径。求出每个点后的路径后,一次遍历便可知树的中心点。

4.例题:

题目描述:

给定一棵树,树中包含 n个结点(编号1~n)和 n−1 条无向边,每条边都有一个正权值。请你在树中找到一个点,使得该点到树中其他结点的最远距离最近。输出该节点以及最近距离。(若存在多个点的距离相同,则输出编号较小的一个)

题目分析:

有data数组表述每个点出发的最大距离,因此我们在第2次和第3次dfs的过程中,与dis数组比较即可。并且在第3次dfs时,把最小距离求出来。

正确代码:

#include<bits/stdc++.h>
using namespace std;
int n,data[100001],dl[100001],maxn,maxn2,minn=2e9,tmp,pl;
vector<int> v[10001];
vector<int> w[10001];
void dfs(int x,int fa,int cnt) {
	for(int i=0; i<v[x].size(); i++) {
		int y=v[x][i];
		if(y==fa) continue;
		data[y]=data[x]+w[x][i];
		if(maxn<data[y]) pl=y,maxn=data[y];
		if(cnt==3) {
			maxn2=max(data[y],dl[y]);
			if(maxn2<minn) minn=maxn2,tmp=y;
		}
		dfs(y,x,cnt);
	}
}
int main() {
	cin>>n;
	for(int i=1; i<=n-1; i++) {
		int x,y,z;cin>>x>>y>>z;
		v[x].push_back(y);
		v[y].push_back(x);
		w[x].push_back(z);
		w[y].push_back(z);
	}
	dfs(1,0,1);
	maxn=0;
	memset(data,0,sizeof data);
	dfs(pl,0,2);
	for(int i=1; i<=n; i++)	dl[i]=data[i];
	memset(data,0,sizeof data);
	maxn=0;
	dfs(pl,0,3);
	cout<<tmp<<" "<<minn;
	return 0;
}

二.树的重心:

1.基础概念:

使得最大子树大小最小。那么这个点叫就被叫做树的重心

在线性的序列[1,n]中,我们在考虑用分治思想处理问题时,需对问题进行划分。在划分问题时若要更加均匀,我们选择中点mid可以更加高效。这样得到[1,mid],[mid+1,n]两个子序列,因为子序列中元素的个数<=n/2(向上取整),这样可以把问题复杂度优化到O(logn)

2.求解方法:

显然,要求树的重心,我们可以枚举出每个点为断点时,所产生的最大子树大小。某断点求当前最大子树大小的方法:对该点进行dfs,找到以i为根节点的子树的大小记录到sz[i]中,接着在该点的儿子中找si最大的一个。复杂度为O(n2)

3.例题:

题目描述:

给定一棵树,树中包含 n(n<=1e5)个结点(编号1~n)和 n−1 条无向边,找出树的重心若重心不止一个,则输出编号较小的),以及当前重心下的最大子树大小。

题目分析:

这是一道关于重心的基础题,仅需用重心的求解方法,再用之前讲的求最大子树的方法就可以了。

正确代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
vector<int> v[N];
int d[N],minn=N,n;//d数组记录当前节点子树的大小
int res,id;//id记录重心,minn为重心下最大子树的大小
void dfs(int x,int fa) {
	d[x]=1;
	int res=0;//开始找以x为根的最大子树
	for(int i=0; i<v[x].size(); i++) {
		int y=v[x][i];
		if(y==fa) continue;
		dfs(y,x);
		d[x]+=d[y];
		res=max(d[y],res);//打擂台找几个子树中的最大值
	}
	res=max(res,n-d[x]);//除最大字数为,剩下的子树
	if(minn>res) minn=res,id=x;
}
int main() {
	cin>>n;
	for(int i=1; i<n; i++) {
		int x,y;cin>>x>>y;
		v[x].push_back(y);
		v[y].push_back(x);
	}
	dfs(n,0);
	cout<<id<<" "<<minn;
	return 0;
}

4.重心的性质:

设mss(u)表示以u为重心的最大子树,s0(u)表示以u为根的子树大小,su(v)表示以u为根的的子树v大小。

性质1:

重心点的最大子树大小不大于整棵树大小的一半。

证明:

设u为重心,v为u的最大子树。可以得出:s0[u]-su[v]>=su[v] ,即 su[v]<=s0[u]/2在整颗树中,存在s0[u]=n,所以su[v]<=n/2得证以某点为根,最大子树大小不超过n/2的都是树的重心

常用推导:

Ⅰ.以某点为根,最大子树大小不超过n/2的一定是树的重心。

Ⅱ.以root为根的有根树中,树的重心一定在其最大的一颗子树内。具体来讲,假设y为root的最大子树的儿子,那么重心一定在tp[y]->root的这一条链中(tp[y]表示子树y的重心)。

性质2:

非空树有且仅有1-2个重心。当有两个重心时,树定有偶数个节点,且两个重心相邻。

证明:

假设u、v为树上两个重心,u,v分别为对方最长链上的点。此时:mss[u]=mss[v]又设k为两个重心之间存在的点数。由mss[u]=su[v]+k,mss[v]=sv[u]+k,推出sv[u]=su[v]。在k个点中选择中点p,此时,mss[p]=max(su[v]+k/2,sv[u]+k/2) >=su[v]+k,当且仅当k=0时,不等式成立。重心u、v之间必不可能有点。所以若有两个重心,则重心必然相邻。

性质3:

树中所有点到重心的距离和最小,反过来距离和最小的点一定是重心。

证明:

当前重心为u。mss[u]=su[v]。假设重心从u移动到v,mss[v]=sv[u],可得1类节点到重心的距离加1,2类节点到重心的距离减少1,因此当增加部分sv[u] 小于 减少部分 sv[u]时,距离和减少所以当su[v]>sv[u]时,重心移动,得到mss更小。反之若当前mss已经最小,则无法再产生一个更小距离和。

性质4:

往树上增加或减少一个叶子,如果原节点数是奇数,那么重心可能增加一个,原重心仍是重心;如果原点数是偶数,重心可能减少一个,另一个重心仍是重心。节

性质5:

把两棵树通过一条边相连得到一棵新的树,则新的重心在较大的一棵树一侧的连接点与原重心之间的简单路径上。如果两棵树大小一样,则重心就是两个连接点。

5.例题:

①子树的重心

题目描述:

输入一棵树,判断每一棵子树的重心是哪一个节点。第一行输入n,q。n表示树的节点个数,q表示询问次数。第二行n-1个数,分别表示从节点2开始,各节点的父亲节点。后面q行,每行一个数x,表示询问当前以x为根的子树中,树的重心位置。(n,q<=3e5)

题目分析:

本题若对每一次询问都查询一遍子树的重心,那么复杂度为O(nq)。在我们求一颗树T的重心时,根据推导2知道,重心一定在最大子树的重心到该树T的根这一条链上。所以我们如果知道最大子树的重心,此时就可以遍历这一条链上的点,根据推导1,只要该点满足其最大子树大小不超过n/2,那么一定是重心。所以我们可以dfs下去,先求出小的子树重心,回溯时再把当前的重心进行记录即可。复杂度O(n+q)

正确代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
vector<int> v[N];
int d[N],minn=N,res,id[N],n,t[N],s[N],q;
void dfs(int x) {
	d[x]=1;
	int res=0;
	for(int i=0; i<v[x].size(); i++) {
		int y=v[x][i];
		dfs(y);
		d[x]+=d[y];
		if(d[y]>d[id[x]]) id[x]=y;//找子树最大的“儿子”
	}
	if(id[x]==0) {//叶子节点的重心就是自己
		t[x]=x;
		return;
	}
	t[x]=t[id[x]];//从“儿子”的重心调到自身,满足条件且靠近x
	while(d[t[x]]*2<d[x]) t[x]=s[t[x]];//向上找重心
}
int main() {
	cin>>n>>q;
	for(int i=2; i<=n; i++) {
		int x;cin>>x;
		s[i]=x;
		v[x].push_back(i);
	}
	dfs(1);//有根树,往下进行dfs
	for(int i=1; i<=q; i++) {
		int y;cin>>y;
		cout<<t[y]<<endl;
	}
	return 0;
}
//d[i]:表示以i为根的子树大小
//s[i]:表示节点i的父亲节点
//id[i]:表示以i为根的有根树的最大子树
//t[i]:表示以i为根的有根数的重心

②唯一的重心

题目描述:

给定一棵节点数为 n(n<=3e5) 的树 , 删一条边然后加上一条边 , 使得该树的重心唯一 。(删掉的边和加上的边可以是同一条)输出删边与加边信息,本题多测。

题目分析:

若存在一个重心,删边与加边都可以是同一条边。若不止一个重心,引理性质2,最多存在两个重心,且重心直接相连。假设两重心分别为idx,idy。要保证只留下一个重心,那就应当对某个重心子树idx进行修改,删除其叶子节点的一条边,且将该叶子节点直接连到另一个重心idy上即可。

正确代码:

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,minn=N,z1,z2;//z1、z2两个重心
int u,sz[N],f[N];//u是断开的叶子节点
vector<int>v[N];
void dfs(int x,int fa) {
	sz[x]=1,f[x]=fa;
	int res=0;
	for(int i=0; i<v[x].size(); i++) {
		int y=v[x][i];
		if(y==fa) continue;
		sz[x]+=sz[y];
		res=max(res,sz[y]);
	}
	res=max(res,n-sz[x]);
	if(minn>res) z1=x,z2=0,minn=res;
	else if(minn==res) z2=x;
}
void dfs1(int x,int fa) {
	if(v[x].size()==1) u=x;
	for(int i=0; i<v[x].size(); i++) {
		int y=v[x][i];
		if(y==fa) continue;
		dfs1(y,x);
	}
}
int main() {
	int t;
	cin>>t;
	while(t--) {
		cin>>n;
		for(int i=1; i<n; i++) {
			int x,y;cin>>x>>y;
			v[x].push_back(y);
			v[y].push_back(x);
		}
		dfs(1,0);
		if(z2==0) {//只有一个重心,就直接输出
			cout<<"1 "<<v[1][0]<<endl<<"1 "<<v[1][0]<<endl;
			continue;
		}
		if(f[z1]!=z2) swap(z1,z2);//保证只有一个重心在子树上遍历
		dfs1(z1,z2);
		cout<<u<<" "<<f[u]<<endl<<u<<" "<<z2<<endl;
		for(int i=1; i<=n; i++) {
			f[i]=0,v[i].clear();
		}
		minn=N,z1=z2=0;
	}
	return 0;
}

③会议

题目描述:

有一个村庄居住着 n 个村民,有 n−1 条路径使得这 n 个村民的家联通,每条路径的长度都为 1。现在村长希望在某个村民家中召开一场会议,村长希望所有村民到会议地点的距离之和最小,那么村长应该要把会议地点设置在哪个村民的家中,并且这个距离总和最小是多少?若有多个节点都满足条件,则选择节点编号最小的那个点。

题目分析:

求所有点到某点的距离和,根据重心性质3,显然是到重心最小,因此求出重心,在进行距离和统计即可。

正确代码:

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5;
int n,minn=N,idx,idy,sum,u,sz[N];//sz数组记录当前节点的子树大小
vector<int>v[N];
void dfs(int x,int fat) {
	sz[x]=1;
	int res=0;
	for(int i=0; i<v[x].size(); i++) {
		int y=v[x][i];
		if(y==fat) continue;
		dfs(y,x);
		sz[x]+=sz[y];
		res=max(sz[y],res);
	}
	res=max(res,n-sz[x]);
	if(minn>res) idx=x,minn=res;
	else if(minn==res&&idx>x) idx=x;
}
void dfs1(int x,int fat,int num) {
	sum+=num;
	for(int i=0; i<v[x].size(); i++) {
		int y=v[x][i];
		if(y==fat) continue;
		dfs1(y,x,num+1);
	}
}
int main() {
	cin>>n;
	for(int i=1; i<n; i++) {
		int x,y;cin>>x>>y;
		v[x].push_back(y);
		v[y].push_back(x);
	}
	dfs(1,0);//找重心
	dfs1(idx,0,0);//统计距离和
	cout<<idx<<" "<<sum;
	return 0;
}

树形结构(1 基础):https://blog.csdn.net/Archie28/article/details/140532542

树形结构(2 树的直径):https://blog.csdn.net/Archie28/article/details/140532713

树形结构(总):https://blog.csdn.net/Archie28/article/details/140504428

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1945954.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

运筹学笔记

计算的时间问题&#xff01;计算机解决了计算量的问题&#xff01; 计算机的发展对运筹学研究起到了极大的促进作用。 运筹学的一个特征之一是它常常会考虑寻求问题模型的最佳解决方案&#xff08;称为最优解&#xff09;。 没有人能成为运筹学所有方面的专家。 分析学越来越流…

反弹shell的方式——之NC反弹shell

反弹shell是指在攻击机监听某个端口&#xff0c;然后通过目标连接攻击机监听的端口&#xff0c;在攻击机反弹得到目标机的shell。通常在目标网络有防火墙或者其他因素限制&#xff0c;导致无法持续控制目标&#xff0c;或者执行命令受阻等情况时需要进行反弹shell 常见的反弹s…

Null和 Undefined 两者区别?

1、 Undefined 和 null 的 区 别 首 先 Undefined 和 Null 都 是 基 本 数 据 类 型 &#xff0c; 这 两 个 基 本 数 据 类 型 分 别 都 只 有 一 个 值 &#xff0c; 就 是 undefined 和 null。 2、undefined 代 表 的 含 义 是 未 定 义 &#xff0c; null 代 表 的 含 义 …

Python Flask入门到精通:详细教程和实战案例

前言 Flask是一个轻量级的Web框架&#xff0c;用于快速开发Web应用程序。它的设计理念是简洁、灵活和易于扩展&#xff0c;非常适合于从简单的单页应用到复杂的大型项目。通过Flask&#xff0c;可以创建各种Web应用程序&#xff0c;比如博客、电子商务网站、RESTful API等。 …

META 备受期待的 Llama 3 405B 即将发布

本心、输入输出、结果 文章目录 META 备受期待的 Llama 3 405B 即将发布前言Llama 3 405B或许会彻底改变专用模型的数据质量Llama 3 405B将形成新的模型生态系统:从基础模型到专家组合Llama 3 405B有最高效 API 的竞争Llama 3 405B 基准测试META 备受期待的 Llama 3 405B 即将…

韦东山嵌入式linux系列-具体单板的按键驱动程序(查询方式)

1 GPIO 操作回顾 &#xff08;1&#xff09;使能模块&#xff1b; &#xff08;2&#xff09;设置引脚的模式&#xff08;工作于GPIO模式&#xff09;&#xff1b; &#xff08;3&#xff09;设置GPIO本身&#xff08;输入/输出&#xff09;&#xff1b; &#xff08;4&…

Linux_make/Makefile的理解

1.make是一个命令&#xff0c;makefile是一个文件, 依赖关系和依赖方法. a.快速使用一下 i.创建一个Makefile文件(首字母也可以小写) b.依赖关系和依赖方法 i.依赖关系: 我为什么要帮你? mybin:mytest.c ii.依赖方法: 怎么帮? gcc -o mybin mytest.c make之前要注意先创建…

UE4-构建光照后导入的静态网格体变黑

当我们将我们的静态网格体导入到项目当中的时候&#xff0c;此时我们进行重新构建光照&#xff0c;我们在从新构建完光照后&#xff0c;会发现我们的静态网格体全部变黑了&#xff0c;此时是因为没有设置光照贴图分辨率和坐标索引引起的。 将General Settings中的L…

Unite 上海 强势回归

​​​ 他回归了 Unite 大会是一年一度的 Unity 全球开发者盛会。今年&#xff0c;Unite 将于 7 月盛夏点亮上海外滩。此次盛会&#xff0c;我们将以“团结”为核心&#xff0c;凝聚全球 3000 多位 Unity 社区精英的力量&#xff0c;共同开启 Unity 技术的新纪元。 在这里&am…

【C++】透析类和对象(上)

有不懂的&#xff0c;可翻阅我之前文章哦&#xff01; 个人主页&#xff1a;CSDN_小八哥向前冲 所属专栏&#xff1a;C入门 目录 类的定义 访问限定符 类域 类的实例化 实例化概念 对象大小 this指针 类的默认成员函数 构造函数 析构函数 模拟栈&#xff08;初学者&…

(最最最全)远程服务器连接新手教程-服务器基本指令、连接服务器、安装Anaconda、配置Conda、配置环境、bashrc环境变量修改(为空怎么办)

一、服务器基本指令 ls - 列出当前目录的文件和子目录cd - 改变当前目录pwd - 显示当前目录的路径df - 查看当前内存mkdir - 创建新目录rm - 删除文件cp - 复制文件mv - 移动或重命名文件 https://blog.csdn.net/weixin_43693391/article/details/133984143?ops_request_mis…

Ubuntu20.04版本升级openssh9.8p1方法

一、问题描述&#xff1a; 8.5p1 和 9.7p1 之间的openssh版本漏洞可能会导致linux系统以root身份进行RCE&#xff0c;所以需安装最新版本 二、解决方法&#xff1a; 将当前openssh版本升级到最新的版本即openssh-9.8p1版本&#xff0c;OpenSSL大版本升级且OpenSSH有新稳定版本…

今天我们聊聊C#的并发和并行

并发和并行是现代编程中的两个重要概念&#xff0c;它们可以帮助开发人员创建高效、响应迅速、高性能的应用程序。在C#中&#xff0c;这些概念尤为重要&#xff0c;因为该语言提供了对多线程和异步编程的强大支持。本文将介绍C#中并发和并行编程的关键概念、优点&#xff0c;并…

CSS(二)——CSS 背景

CSS 背景 CSS 背景属性用于定义HTML元素的背景。 CSS 背景属性 Property描述background简写属性&#xff0c;作用是将背景属性设置在一个声明中。background-attachment背景图像是否固定或者随着页面的其余部分滚动。background-color设置元素的背景颜色。background-image把…

秋招突击——7/24——知识补充——JVM类加载机制

文章目录 引言类加载机制知识点复习类的生命周期1、加载2、连接——验证3、连接——准备4、连接——解析5、初始化 类加载器和类加载机制类加载器类加载机制——双亲委派模型 面试题整理1、类加载是什么2、类加载的过程是什么3、有哪些类加载器&#xff1f;4、双亲委派模型是什…

FineBI连接MySQL5.7

一、在FineBI系统管理中&#xff0c;点击【新建数据库连接】 选择MySQL数据库 配置数据库连接&#xff0c;如下&#xff0c;其中数据库名称就是需要连接的目标数据库

STM32工业物联网系统教程

目录 引言环境准备工业物联网系统基础代码实现&#xff1a;实现工业物联网系统 4.1 数据采集模块 4.2 数据处理与分析模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;工业监测与优化问题解决方案与优化收尾与总结 1. 引言 工业物联网&#xff08…

QCefView 在Clion+vs2022下编译

目录 QCefView 的编译Note下载代码方式一:可直接通过github下载方式二: csdn下载编译代码1. 解压文件2. 按照规定重新放置代码文件3. 将cef 的zip 放入CefViewCore中的dep文件夹内4. 使用Clion打开QCefView工程文件夹测试代码附QCefView 的编译 Note 需要使用VS2022 编译,VS…

配置linux客户端免密登录服务端linux主机的root用户

在192.168.30.129端口&#xff0c;对192.168.30.130端口进行免密登录 登录成功

[嵌入式Linux]-常见编译框架与软件包组成

嵌入式常见编译框架与软件包组成 1.嵌入式开发准备工作 主芯片资料包括&#xff1a; 主芯片资料 主芯片开发参考手册&#xff1b;主芯片数据手册&#xff1b;主芯片规格书&#xff1b; 硬件参考 主芯片硬件设计参考资料&#xff1b;主芯片配套公板硬件工程&#xff1b; 软件…