解:因为这一定是一棵树,所以我们多画几次图,就会发现所有的最长路径中心点都一样,且中心点把这条最长路径分成两段等长的路。
那么做法就很简单啦,先求出图的最长路径长度(称为直径),然后找到中心点(如果最长路径长度为偶数的话,就新建一个点,连上中间的两个点,并把原来两点间的路径删去),然后做一次dfs,那些到中心点的距离为半径的,这个点包括它到中心点的路径上的点都是在最长路径上的。
求最长路径有两种方法:
1.随便取一个点为根,先做一次dfs,再找一个深度最大的点为根,再做一次dfs就可以得到最大的深度了(即为最长路径)。
2.这个有点像拓扑排序,我们每次删除都把度为1的点以及相连的边删去,直到剩下点的数目小于等于二,最后最长路径的长度即为删除的次数(删掉一批度为零的点算一次)*2+剩余的点数目。原理其实就是我们每次把最长链的两端删掉,直到没法删为止,那么删除次数*2+剩余点即为答案。
那么看一下具体程序吧。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; struct ding{ int to,next; }edge[500000]; int ch,nex,cnt,maxdep2,maxdep=0,n,dep[300000],dep2[300000],head[300000],root; bool b[300000]; void dfs(int x,int d) { dep[x]=d; if (d>maxdep) {maxdep=d;ch=x;} for (int i=head[x];i;i=edge[i].next) if (dep[edge[i].to]==0) dfs(edge[i].to,d+1); } int dfs2(int x,int d) { if (d==maxdep/2) return x; for (int i=head[x];i;i=edge[i].next) if (dep[edge[i].to]<dep[x]) return (dfs2(edge[i].to,d+1)); } void dfs3(int x) { b[x]=true; for (int i=head[x];i;i=edge[i].next) //保证那个点之前是没被更新的过,且那个点是当前点的父节点 if ((!b[edge[i].to])&&(dep[edge[i].to]<dep[x])) dfs3(edge[i].to); } void add(int u,int v){edge[++cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt;} int main() { scanf("%d",&n); int s,t; for (int i=1;i<=n-1;i++) { scanf("%d%d",&s,&t); add(s+1,t+1);add(t+1,s+1);//为了方便处理 } dfs(1,1); int ch2=ch; ch=0; memset(dep,0,sizeof dep); maxdep=0; dfs(ch2,1); //用两次dfs得出最长链长度 int now=dfs2(ch,1); //从链尾返回,找到中心点的前一个点 //分类讨论 for (int i=head[now];i;i=edge[i].next) if (dep[edge[i].to]<dep[now]) nex=edge[i].to; //nex代表当前找到的点后面的点。 //如果路的长度为偶数的话,我们就需要加边,删边,加点,这里我加了一个n +1的点 if (maxdep%2==0) {add(nex,n+1);add(n+1,nex);add(n+1,now);add(now,n+1);root=n+1;} else root=nex; //如果长度为奇数,那么中心点就是nex //删边 if (maxdep%2==0) { for (int i=head[now];i;i=edge[i].next) if (edge[edge[i].next].to==nex) { edge[i].next=edge[edge[i].next].next; break; } for (int i=head[nex];i;i=edge[i].next) if (edge[edge[i].next].to==now) { edge[i].next=edge[edge[i].next].next; break; } } //得到半径,并进行第三次dfs int de=maxdep/2+1; maxdep=0; memset(dep,0,sizeof dep); dfs(root,1); //如果深度等于半径的话,这条路上的点都属于最长路径,我们更新答案 for (int i=1;i<=n;i++) if (dep[i]==de) dfs3(i); for (int i=1;i<=n;i++) if (b[i]) printf("%d\n",i-1); return 0; }