树的直径
定义
一个树的直径就是树上两个点的距离中最长的距离。
性质
-
树的直径的路径不一定唯一。
-
树的端点一定是度数为 \(1\) 的点。
-
树的直径若有多条,则一定交汇于数的中心(以这个点为根,树的最大深度最小)。
-
树上任意点 \(i\),距离 \(i\) 最远的点,一定是树的直径的端点之一。
求法
-
Floyd 暴力求索。
-
两遍 DFS,利用性质 \(4\),从任意一个点开始 DFS,找到距离这个点最远的点 \(x\),然后从 \(x\) 开始 DFS,找到最远的点 \(y\),则点 \(x\) 和 \(y\) 为数的直径的两端。不过在取最大值的时候,不能写当前长度大于最大长度,要大于等于,因为后面的可能长度一样,但是点要重新取。
例题
Luogu-B4016
思路
模板题,套上面的思路。
代码
#include <bits/stdc++.h>
using namespace std;
int n, x, mx;
bool vis[100005];
vector<int> g[100005];
void dfs(int u, int sum) {
vis[u] = 1;
if (sum >= mx) {
mx = sum;
x = u;
}
for (int i = 0; i < g[u].size(); ++i) {
int v = g[u][i];
if (!vis[v]) {
dfs(v, sum + 1);
}
}
return;
}
int main() {
cin.tie(0)->sync_with_stdio(false);
cin >> n;
for (int i = 1, x, y; i < n; ++i) {
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1, 0);
fill(vis + 1, vis + n + 1, 0);
dfs(x, 0);
cout << mx;
return 0;
}
HDU-2196
思路
方法一:
直接暴力 DFS。
方法二:
利用树的直径的性质 \(4\),找出两个端点 \(x\),\(y\),最远的点一定是它们当中的一个,所以从它们开始跑最短路 \(disx_i\) 和 \(disy_i\),然后答案就是 \(\max(disx_i , disy_i)\)。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+5;
int n,x,mx,a,b,dis[N][2];
bool vis[N];
struct Node{
int v,w;
};
vector<Node> g[N];
void dfs(int u,int sum)
{
vis[u]=1;
if(sum>=mx)
{
mx=sum;
x=u;
}
for(int i=0;i<g[u].size();++i)
{
int v=g[u][i].v,w=g[u][i].w;
if(!vis[v])
{
dfs(v,sum+w);
}
}
return;
}
void dfs2(int u,int f)
{
vis[u]=1;
for(int i=0;i<g[u].size();++i)
{
int v=g[u][i].v,w=g[u][i].w;
if(!vis[v])
{
dis[v][f]=dis[u][f]+w;
dfs2(v,f);
}
}
return;
}
signed main()
{
cin.tie(0)->sync_with_stdio(false);
while(cin>>n)
{
for(int i=1;i<=n;++i)
{
g[i].clear();
vis[i]=0;
dis[i][0]=dis[i][1]=0;
}
a=0,b=0;
for(int i=2,y,z;i<=n;++i)
{
cin>>y>>z;
g[i].push_back({y,z});
g[y].push_back({i,z});
}
mx=0,x=0;
fill(vis+1,vis+n+1,0);
dfs(1,0);
a=x;
fill(vis+1,vis+n+1,0);
dfs(x,0);
b=x;
fill(vis+1,vis+n+1,0);
dfs2(a,0);
fill(vis+1,vis+n+1,0);
dfs2(b,1);
for(int i=1;i<=n;++i)
{
cout<<max(dis[i][0],dis[i][1])<<'\n';
}
}
return 0;
}
Codeforces-734E
思路
我们先将一个颜色且联通的连通块缩成一个点,
然后,此时的树必定是黑白交替的,那么最终的答案就是 \(\lceil \frac{直径}{2} \rceil\)。
注意
两遍 DFS 的做法也有一个缺点,就是不能有负边权。
hack数据:
5
1 2 5
2 4 -10
4 5 20
1 3 4
正确答案:
19
实际输出:
20
树形DP求解
-
树的中心的最长链和次长链对应的端点就是树的直径的两端。
-
维护点 \(i\) 的最长路 \(dis1_i\) 和 \(dis2_i\),取 \(\max(dis1_i + dis_2_i)\) 即树的直径。
-
除了共点 \(i\),两条路径没有交点,树的中心要进行枚举。
-
可以处理负边权。

浙公网安备 33010602011771号