C++树形结构(2 树的直径)

目录

1.定义:

2.直径的性质:

3.树的直径求解方法:

4.直径端点求解方法:

朴素方法:

优化方法:

5.例题:

6.直径公共点:

7.例题:

8.去掉再加上:

9.例题:


1.定义:

树的直径是树上两点间距离的最大值。即树中最远的两个节点之间的距离被称为树的直径,连接这两点的路径被称为树的最长链

最长链:4-2-1-7-6-3

所以这颗树的直径是15,直径路径为4-2-1-3-6

2.直径的性质:

直径的性质1:直径的端点一定是叶子节点

直径的性质2:任意点的最长链端点一定是直径端点

直径的性质3:如果一棵树有多条直径且边权都为正,那么它们必然相交,且有极长连续段(可以是一个点,交点为树的中心)

直径的性质4:树T1的直径为x,y,树T2的直径为s,t。现有一边u,v与两颗树相连,新树的直径端点一点是x,y,s,t中的两个

3.树的直径求解方法:

引理性质2:任意点的最长链端点一定是直径端点。方法:我们随意找一个点x,进行dfs找到最长链的端点s,再以端点s做第二遍dfs,此时可以找到直径的第二个端点t。此时端点s到t的距离就是树的直径。

输入一颗无根树,第一行为一个正整数n(n<1e5),表示这颗树有n个节点接下来的n−1行,每行三个正整数u,v,w,表示u,v(u,v<=n)有一条权值为w(w<100)的边相连,求树的直径。 

#include<bits/stdc++.h>
using namespace std;
int n,data[100005],pl,maxn;
vector<int> v[100005];
vector<int> w[100005];
void dfs(int x,int fa) {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(data[y]>maxn) maxn=data[y],pl=y;//记录端点dfs(y,x);}
}
int main() {cin>>n;for(int i=1; i<n; 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);//寻找直径memset(data,0,sizeof data);//清空距离dfs(pl,0);//从pl出发寻找端点cout<<maxn;return 0;
}

4.直径端点求解方法:

我们通过记录父亲节点的方式能够把直径上的所有点全部记录下来。在树中,直径端点是常用点(假设端点为s,t),我们树上任意一点p所能到的最大距离,只有可能是到ps或pt

那如何找到所有点到两个直径端点的距离?

朴素方法:

求出直径端点后,以每个点为根做dfs,找到根节点到端点的距离。复杂度O(N2)。

优化方法:

第一次从任意点出发,必然能到达直径的一个端点s。第二次从s点进行dfs找到端点t,此时记录所有点到s的距离。第三次从t点进行dfs,记录所有点到t的距离。复杂度:O(n)

5.例题:

题目描述:

 输入一颗无根树,第一行为一个正整数n(n<1e5),表示这颗树有n个节点接下来的n−1行,每行三个正整数u,v,w,表示u,v(u,v<=n)有一条权值为w(w<100)的边相连,输出各个点到左右端点的距离。(默认左端点为编号小的点,右端点为编号大的点。)

题目分析:

我们需要多次求树的直径。通过第一次dfs从根寻找第一个端点pl,再通过第二次dfs从pl寻找第二个端点pr,并记录所经过的距离,最后通过第三次dfs记录每个点到左右端点的距离。多次求树的直径,有很多重复操作,包括清空最长链长度,以及重置距离数组,我们可以放到循环中,这样就不容易遗忘初始化。同时我们还可以记录当前是第几次dfs,到指定次数dfs时才更新信息。

正确代码:

#include <bits/stdc++.h>
using namespace std;
vector <int> v[100002];
vector<int> w[100002];
int n,maxn,sum,data[100002],dl[100002],s[100002],l,r;
void dfs1(int x,int fa) {if(data[x]>maxn) maxn=data[x],sum=x;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];dfs1(y,x);}
}
void dfs2(int x,int fa) {dl[x]=data[x];if(data[x]>maxn) maxn=data[x],sum=x;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];dfs2(y,x);}
}
void dfs3(int x,int fa) {s[x]=data[x];if(data[x]>maxn) maxn=data[x],sum=x;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];dfs3(y,x);}
}
int main() {cin>> n;for(int i=1; i<n; 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);}dfs1(1,0);memset(data,0,sizeof data);maxn=0,l=sum;dfs2(l,0);memset(data,0,sizeof data);maxn=0,r=sum;dfs3(r,0);if(l>r) swap(dl,s),swap(l,r);//保证做端点较小for(int i=1; i<=n; i++) cout<<i<<" "<<dl[i]<<" "<<s[i]<<endl;return 0;
}

6.直径公共点:

以当一颗树存在多条直径时,引理性质3,公共边一定连续,因此可以直接对公共点/边进行求解

公共点公共边的求法:

找到直径左右端点s,t,从左往右遍历直径上的点进行dfs,如果某点r在直径外找到一点与到右端点t距离相同,点r右边的点一定不是公共点。同理,从右往左遍历直径上的点进行dfs,如果某点l在直径外找到一点与到左端点s距离相同,l左边的点一定不是公共点。此时,l->r就是我们直径的公共点。因此我们只需要找到公共点边界l,r即可。使得l尽可能靠右,r尽可能靠左。

7.例题:

题目描述:

 给定一棵树,树中包含 n(n<=1e5)个结点(编号1~n)和 n−1 条无向边,每条点都有一个权值c(1<=c<=100)。请找出所有直径的公共点权值和。(第一个点权值为0)

题目分析:

按照直径公共点求解的方法进行操作。

1.找到直径左右端点lp,rp;

2.找到直径上的点x到左端点lp的距离ld[x],到右端点rp的距离rd[x];

3.找到直径上的点x到非直径点(且不通过直径点)的距离dis[x]

4.从直径左端点开始向右端点扫描,如果dis[i]=rd[i],则停下,找到公共点右区间r=i

5.从直径右端点开始向左端点扫描,如果dis[j]=ld[j],则停下,找到公共点左区间l=j

6.计算直径上i到j的点权和得出答案。

真确代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2E5+5;
int n,c[N];
vector<int> v[N];
int dl[N],dr[N],d[N],dis[N];//记录各点距离信息
int pre[N],a[N],vis[N],tot,maxn,t,pl,pr,l,r;//记录直径上的信息
bool flag;
void dfs(int x,int fa,int cnt) {if(cnt==3) pre[x]=fa;//记录直径路径for(int i=0; i<v[x].size(); i++) {int y=v[x][i];if(y==fa) continue;d[y]=d[x]+1;if(maxn<d[y]) maxn=d[y],t=y;if(cnt==2) dl[y]=d[y];if(cnt==3) dr[y]=d[y];dfs(y,x,cnt);}
}
void dfs2(int x,int fa) {for(int i=0; i<v[x].size(); i++) {int y=v[x][i];if(y==fa || vis[y]==1) continue;dfs2(y,x);dis[x]=max(dis[x],dis[y]+1);}
}int main() {cin>>n;for(int i=1; i<n; i++) {int x,y,z;cin>>x>>y>>z;v[x].push_back(y);v[y].push_back(x);c[i+1]=z;}t=1;for(int i=1; i<=3; i++) {//进行3次dfsmaxn=0;memset(d,0,sizeof d);dfs(t,0,i);if(i==1) pl=t;//获取直径的左端点if(i==2) pr=t;//获取直径的右端点}for(int i=t; i; i=pre[i]) {vis[i]=1;a[++tot]=i;//从左到右标记直径上的点}for(int i=2; i<tot; i++) dfs2(a[i],0);//获取点x能到的不在直径上的最远点l=a[1],r=a[tot];//使得右端点r靠左,左端点l靠右for(int i=2; i<tot; i++) {int x=a[i];if(dis[x]==dr[x] && flag==false){r=x;flag=true;}if(dis[x]==dl[x]) l=x;}if(l==a[1]&&r==a[tot]) {//没有公共点cout<<0;return 0;}int ans=c[r];while(l!=r) ans+=c[l],l=pre[l];cout<<ans;return 0;
}

8.去掉再加上:

性质4分析:

uv连接后有两种情况1.新直径不过uv,即现直径为st或为xy。2.新直径过uv,则现直径为max(vs,vt)+max(ux,uy)+uv。这两种情况都能保证新直径端点为x,y,s,t中的任意两个。新直径为以上三个中最大值。

连边uv求新树直径最小:

引理性质4可知:

st与xy不变,此时只能减下过uv的直径大小。以max(vs,vt)为例,要使该值最小,则v应当在树的中心位置,这样vs与vt越均衡。同理u也应该在T2的树的中心位置。

连边uv求新树直径最大:与前面一致,以max(vs,vt)为例,要使得该值最大,则v应当选择直径端点位置。因此uv选择各自直径的端点位置时,直径最大。

9.例题:

题目描述:

 给定一棵 n (1<=n<=1e4)个点构成的树。树中每条边的长度均为 1。现在,需要你去掉树中的一条边,然后再给树加上一条边,使得图形仍是树。请计算,新树的直径的最小可能值。(新树可以和原来的树完全一样)。

题目分析:

由性质4,我们知道连接时需要连接两棵树的中心,才能使得新树直径尽可能小。所以连接点很好处理。对于断开点,我们必然只有断开直径上的边,否则直径不可能变小,具体直径上哪一条边,我们可以进行枚举。

真确代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1E4+5;
vector<int>v[N];
int n,ans,maxn,t,pre[N],d[N];
bool vis[N][N];
void dfs(int x,int fa,int cnt,int k) {if(cnt==2&&k==0) pre[x]=fa;for(int i=0; i<v[x].size(); i++) {int y=v[x][i];if(y==fa||vis[x][y]) continue;d[y]=d[x]+1;if(d[y]>=maxn) maxn=d[y],t=y;dfs(y,x,cnt,k);}
}
int get(int x,int k) {t=x;for(int i=1; i<=2; i++) {maxn=0;memset(d,0,sizeof d);dfs(t,0,i,k);}return maxn;
}
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);}ans=get(1,0);int ans=maxn,res;for(int i=t; i; i=pre[i]) {if(i==0||pre[i]==0) continue;vis[i][pre[i]]=vis[pre[i]][i]=1;int lena=get(i,1),lenb=get(pre[i],1);res=max(max(lena,lenb),(lena+1)/2+(lenb+1)/2+1);ans=min(ans,res);res=0;vis[i][pre[i]]=vis[pre[i]][i]=0;}cout<<ans;return 0;
}

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

树形结构(3 树的中心、重心):https://blog.csdn.net/Archie28/article/details/140532797

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

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

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

相关文章

最新版kubeadm搭建k8s(已成功搭建)

kubeadm搭建k8s&#xff08;已成功搭建&#xff09; 环境配置 主节点 k8s-master&#xff1a;4核8G、40GB硬盘、CentOS7.9&#xff08;内网IP&#xff1a;10.16.64.67&#xff09; 从节点 k8s-node1&#xff1a; 4核8G、40GB硬盘、CentOS7.9&#xff08;内网IP&#xff1a;10…

框架使用及下载

Bootstrap5 安装使用 | 菜鸟教程 (runoob.com) https://github.com/twbs/bootstrap/releases/download/v5.1.3/bootstrap-5.1.3-dist.zip&#xff08;下载链接&#xff09; Staticfile CDN&#xff08;html的所有框架合集&#xff09; 直接在w3cschool里面看参考文件进行搜索自…

RHCSA —— 第八节 (编辑器、编辑命令等)

Vi/vim编辑器 vim 编辑器 就是相当于在windows中创建一个记事本&#xff0c;一个word文档里面进行编辑所需要的内容。在linux中编辑文本文件&#xff0c;包括但不限于编辑源代码、配置文件、日志文件等文件内容。 三种模式 这是在编辑器中存在三种模式&#xff1a;命令模式、…

[经验] 驰这个汉字的拼音是什么 #学习方法#其他#媒体

驰这个汉字的拼音是什么 驰&#xff0c;是一个常见的汉字&#xff0c;其拼音为“ch”&#xff0c;音调为第四声。它既可以表示动词&#xff0c;也可以表示形容词或副词&#xff0c;意义广泛&#xff0c;经常出现在生活和工作中。下面就让我们一起来了解一下“驰”的含义和用法。…

Deepfake detection【Datawhale AI夏令营】数据增强方法

deepfake detection比赛链接https://www.kaggle.com/competitions/multi-ffdi 训练分类模型判别图片是否为AI生成图片&#xff0c;探究不同数据增强方法对模型表现的影响。 1、数据增强方法 图像分类任务中常见的数据增强方法&#xff1a; &#xff08;1&#xff09; 几何变换…

【BUG】已解决:xlrd.biffh.XLRDError: Excel xlsx file; not supported

已解决&#xff1a;xlrd.biffh.XLRDError: Excel xlsx file&#xff1b; not supported 目录 已解决&#xff1a;xlrd.biffh.XLRDError: Excel xlsx file&#xff1b; not supported 【常见模块错误】 错误原因 解决办法&#xff1a; 欢迎来到英杰社区https://bbs.csdn.net/…

深入解析HNSW:Faiss中的层次化可导航小世界图

层次化可导航小世界&#xff08;HNSW&#xff09;图是向量相似性搜索中表现最佳的索引之一。HNSW 技术以其超级快速的搜索速度和出色的召回率&#xff0c;在近似最近邻&#xff08;ANN&#xff09;搜索中表现卓越。尽管 HNSW 是近似最近邻搜索中强大且受欢迎的算法&#xff0c;…

详解SVN与Git相比存在的不足

原文全文详见个人博客&#xff1a; 详解SVN与Git相比存在的不足截至目前&#xff0c;我们已既从整理梳理的SVN和Git在设计理念上的差异&#xff0c;也重点对二者的存储原理和分支管理理念的差异进行深入分析。这些差异也直接造成了SVN和Git在分支合并、冲突解决、历史记录管理…

山西大学—双一流大学,考数据结构+C语言。山西大学计算机考研考情分析!

山西大学&#xff08;Shanxi University&#xff09;&#xff0c;位于山西省太原市&#xff0c;是中国办学历史最悠久的高等学府之一&#xff0c;是国家“双一流”建设高校&#xff0c;教育部和山西省人民政府共同建设的“部省合建高校”&#xff0c;山西省重点建设大学&#x…

JVM监控及诊断工具-命令行篇-jstack命令介绍

加粗样式 JVM监控及诊断工具-命令行篇04-jstack&#xff1a;打印JVM中线程快照 一 基本情况二 基本语法 一 基本情况 jstack(JVM Stack Trace)&#xff1a; 用于生成虚拟机指定进程当前时刻的线程快照(虚拟机堆栈跟踪)。 线程快照就是当前虚拟机内指定进程的每一条线程正在执…

新手小白的pytorch学习第七弹------分类问题模型

目录 1. 准备分类数据1.1 输入和输出的形状 shape1.2 将数据转换为张量&#xff0c;同时将我们的数据集转换为训练集和测试集 2 创建模型方法一&#xff1a;自定义forward()方法二&#xff1a;nn.Sequential()方法三&#xff1a;自定义forward()nn.Sequential() 用 pytorch 使用…

基于A律压缩的PCM脉冲编码调制通信系统simulink建模与仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1A律压缩的原理 4.2 PCM编码过程 4.3 量化噪声与信噪比 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2022a 3.部分核心程序 &#…

python项目读取oracle数据库方法(cx_Oracle库实现)

目录 创建一个python项目&#xff0c;并配置运行环境 查看oracle对应数据库版本&#xff08;该标题下内容只是为了查看版本&#xff0c;不用在意&#xff09; 从oracle官网下载对应版本的oracle客户端 解压下载的压缩包&#xff0c;并获取依赖 将依赖文件导入python项目运…

82. UE5 RPG 实现角色升级系统(下)

书接上回&#xff0c;在上一篇博客里&#xff0c;我们实现了角色升级的基础的功能。给敌人增加的经验奖励配置&#xff0c;并且在敌人死亡时&#xff0c;能够将经验通过事件传递给击杀者&#xff0c;玩家定义了被动技能&#xff0c;在被动技能中接收传递的事件&#xff0c;通过…

iOS 开发包管理之CocoaPods

CocoaPods&#xff08;Objective-C 时期&#xff0c;支持Objective-C和swift&#xff09;&#xff0c;CocoaPods下载第三方库源代码后会将其编译成静态库.a 文件 或动态库框架.framework 文件 的形式&#xff0c;并将它们添加到项目中&#xff0c;建立依赖关系&#xff0c;这种…

Redis实现用户会话

1.分布式会话 (1)什么是会话 会话Session代表的是客户端与服务器的一次交互过程&#xff0c;这个过程可以是连续也可以是时断时续的。曾经的Servlet时代&#xff08;jsp&#xff09;&#xff0c;一旦用户与服务端交互&#xff0c;服务器tomcat就会为用户创建一个session&#…

开源PDF解析工具marker 和 MinerU的解析效果对比

RAG中的文档解析需求&#xff1a;需要的是文档的完整段落&#xff0c;标题&#xff0c;图片&#xff0c;表格。我们希望删除的是md格式&#xff0c;或者josn格式。 MinerU 和 maker恰好。都是能够满足此需求的开源工具。这篇文章分享一下对两者的对比。整理出来目前还存在的问题…

RPG素材Unity7月20闪促限时4折游戏开发资产兽人角色模型动画休闲放置模板物理交互流体水下焦散VR界面UI2D模板场景20240720

今天这个是RPG素材比较多&#xff0c;还有一些休闲放置模板、FPS场景素材、角色模型、动画、特效。 详细内容展示&#xff1a;www.bilibili.com/video/BV1Tx4y1s7vm 闪促限时4折&#xff1a;https://prf.hn/l/0eEOG1P 半价促销&#xff1a;https://prf.hn/l/RlDmDeQ 7月闪促…

谷粒商城实战-Vue学习过程中踩坑记录

一&#xff0c;自闭合的<script>标签 第一次使用Vue&#xff0c;按照步骤引入vue.js&#xff0c;创建div&#xff0c;创建Vue对象&#xff0c;但是未达预期效果。 插值表达式{{name}}没被替换为data对象中的属性值。 F12看了下网页源代码&#xff0c;发现创建Vue对象的…

OpenAI突发新模型GPT-4o mini,GPT-3.5退役!

OpenAI突发新模型&#xff0c;全面取代老去的GPT-3.5——GPT-4o mini&#xff01; 免费用户已可使用GPT-4o mini模型。 GPT-4o mini&#xff0c;能力接近原版GPT-4&#xff0c;价格却要便宜一个数量级&#xff1a; GPT-4o mini:每百万输入tokens&#xff0c;15美分&#xff0…