树的直径个人学习笔记(含例题)

1.树的直径
树的直径定义为一颗树的最远的两个点对d(i,j)对应链的长度.
如何求树的直径?
采用两次dfs的方法。第一次dfs先求出对于1号点来说最远的点u.那么u一定是该直径上的一端.
然后我们进行第二次dfs.以u节点为根,找到最深的子节点v.那么路径(u->v)就是这个树的直径.
代码:
注意注意注意di是全局变量,不能传参,否则会错,要传参的话得是引用类型,每次使用前需要清空.
在第三个函数中(U,V)是引用类型,就是代表找到的两个最远的点啦.
并且得到一个最终的 f a [ u ] fa[u] fa[u]数组,方便还原这个直径.

int depth[maxn];int di = 0;
void dfs1(int u,int f,int &U){
	if(depth[u]>di){
		di = depth[u];
		U = u;
	}
	for(auto [v,w] : G[u]){
		if(v==f) continue;
		depth[v] = depth[u] + w;
		dfs1(v,u,U);
	}
}
int fa[maxn];
void dfs2(int u,int f,int &V){
	fa[u] = f;
	if(depth[u]>di){
		di = depth[u];
		V = u;
	}
	for(auto [v,w] : G[u]){
		if(v==f) continue;
		depth[v] = depth[u] + w;
		dfs2(v,u,V);
	}
}
void find_di(int &U,int &V){
	di = 0;
	memset(depth,0,sizeof(depth));
	dfs1(1,0,U);
	di = 0;
	memset(depth,0,sizeof(depth));
	dfs2(U,0,V);
}

例题1:
P5536 【XR-3】核心城市

来自 https://www.luogu.com.cn/problem/P5536
题目描述
X 国有 n 座城市,n−1 条长度为 1 的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达,显然,城市和道路形成了一棵树。
X 国国王决定将 k 座城市钦定为 X 国的核心城市,这 k 座城市需满足以下两个条件:
1. 这 kk 座城市可以通过道路,在不经过其他城市的情况下两两相互到达。
2. 定义某个非核心城市与这 k 座核心城市的距离为,这座城市与 k 座核心城市的距离的最小值。那么所有非核心城市中,与核心城市的距离最大的城市,其与核心城市的距离最小。你需要求出这个最小值。

思路:贪心地想,该最小值如何得到,在k=1的时候.必然要设定该城市位于树直径的中点上,只有这样才能最小化所有点到该点距离的最大值.
那么继续思考,如何才会使得答案变得更小.必然是选取与该中点连接的某个点u.选取之后答案不会变得更差.
设其他点u到中点mid的距离为dep[u];那么,我们只需要贪心地选取深度为前k-1个.
实现:先求出树的直径两个点(U,V).找到该链上的中点mid.以该mid为根,重建该树,统计每个点最深孩子与自己高度之差.
排序,贪心认为选取前k大的点(一定包含树根,且选择的点一定是连通的,稍加思考即可知道),那么ans[k+1]+1就是答案

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
const int INF = 1e9+7;
typedef long long ll;
typedef pair<int,int> pii;
vector<int> G[maxn];
int depth[maxn];int max_depth[maxn];
int di=0;
void dfs1(int u,int f,int &U){
	if(depth[u]>di){
		U = u;di = depth[u];
	}
	for(auto v : G[u]){
		if(v==f) continue;
		depth[v] = depth[u] + 1;
		dfs1(v,u,U); 
	}
}
int fa[maxn];
void dfs2(int u,int f,int &V){
	fa[u] = f;
	if(depth[u]>di){
		di = depth[u];V = u;
	}
	for(auto v : G[u]){
		if(v==f) continue;
		depth[v] = depth[u] + 1;
		dfs2(v,u,V);
	}
}
void dfs3(int u,int f){
	max_depth[u] = depth[u];
	for(auto v :G[u]){
		if(v==f) continue;
		depth[v] = depth[u] + 1;
		dfs3(v,u);
		max_depth[u] = max(max_depth[u],max_depth[v]);
	}
}
int main(){
//    freopen("1.txt","r",stdin);
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n,k;cin>>n>>k;
	for(int i=1;i<=n-1;i++){
		int u,v;cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	int U,V;
	di = 0;
	dfs1(1,0,U);
	memset(depth,0,sizeof(depth));
	di = 0;
	dfs2(U,0,V);
	int mid = V;
	for(int i=1;i<=(depth[V]+1)/2;i++) mid = fa[mid];
	memset(depth,0,sizeof(depth));
	dfs3(mid,0);
	vector<int> ans(n+1,0);
	for(int i=1;i<=n;i++) ans[i] = max_depth[i] - depth[i];
	sort(ans.begin()+1,ans.end(),greater<int>());
	int tmp = 0;
	for(int i=k+1;i<=n;i++) tmp = max(tmp,ans[i]+1);
	cout<<ans[k+1]+1<<"\n";
}

例题2:逃学的小孩
https://www.luogu.com.cn/problem/P4408
在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
const int INF = 1e9+7;
typedef long long ll;
typedef pair<int,int> pii;
vector<pii> G[maxn];
#define int long long
int depth[maxn];int di = 0;
void dfs1(int u,int f,int &U){
	if(depth[u]>di){
		di = depth[u];
		U = u;
	}
	for(auto [v,w] : G[u]){
		if(v==f) continue;
		depth[v] = depth[u] + w;
		dfs1(v,u,U);
	}
}
int fa[maxn];int max_depth[maxn];
void dfs2(int u,int f,int &V){
	fa[u] = f;
	if(depth[u]>di){
		di = depth[u];
		V = u;
	}
	for(auto [v,w] : G[u]){
		if(v==f) continue;
		depth[v] = depth[u] + w;
		dfs2(v,u,V);
	}
}
int depth1[maxn];
void dfs3(int u,int f){
	for(auto [v,w] : G[u]){
		if(v==f) continue;
		depth1[v] = depth1[u] + w;
		dfs3(v,u);
	}
}
void find_di(int &U,int &V){
	di = 0;
	memset(depth,0,sizeof(depth));
	dfs1(1,0,U);
	di = 0;
	memset(depth,0,sizeof(depth));
	dfs2(U,0,V);
}
signed main(){
//    freopen("1.txt","r",stdin);
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n,m;cin>>n>>m;
	for(int i=1;i<=n-1;i++){
		int u,v,w;cin>>u>>v>>w;
		G[u].push_back({v,w});
		G[v].push_back({u,w});
	}
	int U,V;
	find_di(U,V);
	int ans = 0;
	dfs3(V,0);
	for(int i=1;i<=n;i++){
		ans = max(ans,min(depth1[i],depth[i])+depth[V]);
	}
	cout<<ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

minato_yukina

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

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

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

打赏作者

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

抵扣说明:

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

余额充值