【树的直径】
【树的直径】
求解方法
//搜索思路
(1)任选一点x,求出距离x最远的点y
然后再以y为起点,求出距离y最远的点的距离
//树形DP思路
(2)树的直径maxd就是所有节点往下走的最大距离与次大距离之和中的最大值
即maxd=max(dis1[i]+dis2[i])
(例题)旅游规划
https://www.acwing.com/file_system/file/content/whole/index/content/4184023/
思路
往下走多条路径,往上走一条路径
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010,M=N<<1;
int n;
int e[M],ne[M],h[N],idx;
int dist1[N],dist2[N],nex[N],up[N];
//next1[i] 表示点i往下走的最远路径上的下一个点的编号
int maxd;//树的直径
void add(int x,int y){
e[idx]=y;
ne[idx]=h[x];
h[x]=idx++;
}
//向下走求解最大距离和次大距离->求解树的直径
void dfs_down(int u,int fa){
for(int i=h[u];i!=-1;i=ne[i]){
int j=e[i];
if(j!=fa){//要去掉自己本身这个点
dfs_down(j,u);//先递归后求解:从下而上
int dis=dist1[j]+1;
if(dis>dist1[u]){//比最大距离还要大
dist2[u]=dist1[u];
dist1[u]=dis;
nex[u]=j;
}
else if(dis>dist2[u]){//比最大距离小但是比次大距离大
dist2[u]=dis;
}
}
}
//更新直径长度:最大距离+次大距离
maxd=max(maxd,dist1[u]+dist2[u]);
}
//向上走
void dfs_up(int u,int fa){
for(int i=h[u];i!=-1;i=ne[i]){
int j=e[i];//注意:j是子节点 u是父节点
if(j!=fa){
up[j]=up[u]+1;//从u往上走
//如果走到最高点要向下走:走最长路->不能走原先最长距离的路(j)
//从u往下走最长路
if(nex[u]!=j) up[j]=max(up[j],dist1[u]+1);
//从u往下走次长路
else up[j]=max(up[j],dist2[u]+1);
dfs_up(j,u);//先求解后递归:从上而下
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
//添加无向边
add(a,b);
add(b,a);
}
//随便指定一个点为根节点dfs树即可
dfs_down(0,-1);
dfs_up(0,-1);
//输出位于直径上的所有点
for(int i=0;i<n;i++){
int dis[3]={dist1[i],dist2[i],up[i]};
//排序找出3中方案中最大的两个->判断该点是否经过直径
sort(dis,dis+3);
if(dis[1]+dis[2]==maxd) cout<<i<<"\n";
}
return 0;
}