题目地址:SDUT OJ 3045
这题比赛的时候想的差不多。。但是总是觉得不对。。写了一次就没再写,然后删了。。当时没想到的是第二次求出来的就是最长链。。当时想到的两次bfs找最大值(这一种方法其实结果也对。。TAT。。),还有找到点后在回溯减去重点等等。。但总觉得好像都不太对。。。赛后才知道这题原来是树的直径。。。。。牡丹江区域现场赛的时候遇到过,不过赛后也没看。。。
找树的直径的方法其实就是先任取一点进行bfs,找到最远的一点,这时最远的一点肯定是最长链端点之一,然后再从这一最远点开始bfs,这时另一个端点就找到了,长度就是bfs的深度。
看的其他地方的证明如下:
关键在于证明第一次遍历的正确性,也就是对于任意点u,距离它最远的点v一定是最长路的一端。
如果u在最长路上,那么v一定是最长路的一端。可以用反证法:假设v不是最长路的一端,则存在另一点v’使得(u→v’)是最长路的一部分,于是len(u→v’) > len(u→v)。但这与条件“v是距u最远的点”矛盾。
如果u不在最长路上,则u到其距最远点v的路与最长路一定有一交点c,且(c→v)与最长路的后半段重合(why?),即v一定是最长路的一端。
代码如下:
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
using namespace std;
#define LL __int64
const int INF=0x3f3f3f3f;
int head[110001], cnt, vis[110001], pos, d;
struct node {
int u, v, next;
} edge[210001];
void add(int u, int v)
{
edge[cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt++;
}
struct node1 {
int n, ans;
};
void bfs(int source)
{
memset(vis,0,sizeof(vis));
vis[source]=1;
node1 f1, f2;
queue<node1>q;
f1.n=source;
f1.ans=0;
q.push(f1);
while(!q.empty()) {
f1=q.front();
pos=f1.n;
d=f1.ans;
q.pop();
int u=f1.n;
for(int i=head[u]; i!=-1; i=edge[i].next) {
int v=edge[i].v;
if(!vis[v]) {
f2.n=v;
f2.ans=f1.ans+1;
vis[v]=1;
q.push(f2);
}
}
}
}
int main()
{
int n, i, j, u, v;
while(scanf("%d",&n)!=EOF) {
if(n==1) {
printf("1\n");
continue ;
}
memset(head,-1,sizeof(head));
cnt=0;
for(i=0; i<n-1; i++) {
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
bfs(1);
bfs(pos);
printf("%d\n",d+1);
}
return 0;
}