【题解】
1. 给你一棵树,求树的直径:
先随便取一个点,一遍dfs找到离它最远的点low,再以low为起点做一遍dfs,找到离low最远的点high第二问:实际上每个点只会被访问一次(与直径上的点不能经过有关),也是O(n)
#include<stdio.h> #include<stdlib.h> typedef long long LL; LL w[400005]={0},d[200005]={0}; int v[400005]={0},first[200005]={0},next[400005]={0},f[200005]={0},no[200005]={0}; LL dis=0; int e=0; void tj(int x,int y,LL z) { v[++e]=y; w[e]=z; next[e]=first[x]; first[x]=e; } void dfs(int x,int fa,int& dian) { int i; f[x]=fa; if(dis<d[x]) { dis=d[x]; dian=x; } for(i=first[x];i!=0;i=next[i]) if(v[i]!=fa) { d[v[i]]=d[x]+w[i]; dfs(v[i],x,dian); } } void find(int x,int fa) { int i; if(dis<d[x]) dis=d[x]; for(i=first[x];i!=0;i=next[i]) { if(v[i]!=fa&&no[v[i]]==0) { d[v[i]]=d[x]+w[i]; find(v[i],x); } } } int main() { LL z,ld,rd; int n,i,x,y,low,high,left,right,flag=1,ans=0; scanf("%d",&n); for(i=1;i<n;i++) { scanf("%d%d%lld",&x,&y,&z); tj(x,y,z); tj(y,x,z); } dfs(1,0,low); dis=d[low]=0; dfs(low,0,high); printf("%lld\n",dis); for(i=high;i!=0;i=f[i])//标记某条直径上的点 no[i]=1; left=low;//限定在树的所有直径上的边(连续)的范围 right=high; for(i=f[high];i!=low;i=f[i]) { ld=d[i];//该点到low的距离 rd=d[high]-d[i];//该点到high的距离 dis=d[i]=0; find(i,0); if(dis==ld&&flag==1)//"该点到low所经过的边都不在答案中",将left缩小。这种情况只缩小一次,否则left的范围会被扩大 { left=i; flag=0; } if(dis==rd) right=i;//"该点到high所经过的边都不在答案中",将right缩小 } for(i=right;i!=left;i=f[i]) ans++; printf("%d",ans); return 0; }