两次bfs:
①任意从一个点u出发bfs,设其能到的最远点为v
②从v出发重新bfs,设其能到达的最远点为s
③则树的直径就是v->s
证明:
若能证明从任意一个点出发,bfs到的最远点一定在树的直径的端点上,那么第二次bfs就可以证明一定正确了,下面来证明第一次bfs正确性:
①若选择的点u在直径上,那么能到的最远点v一定是树的直径的端点之一
反证:
若v不是树的直径的端点,设树的直径的端点为v1,v2
那么就说明path(u,v)>path(u,v1),path(u,v)>path(u,v2)
所以path(v1,u)+path(u,v)>path(v1,v2),这说明v1,v2不是树的直径的端点,与题设不符,故"v不是树的直径的端点"不成立
②若选择的点u不在直径上,那么能到的最远点v一定是树的直径的端点之一。这个也很好说明,我们知道树的直径一定是经过根节点的(特殊的根节点如果只有一 个孩子那么就连到根节点为止),即可以理解跨过根节点两边。所以我们在第一次从u bfs的时候,中间一定会bfs到直径上的某点,那么接下来就是又回到了① 情况了。
①任意从一个点u出发bfs,设其能到的最远点为v
②从v出发重新bfs,设其能到达的最远点为s
③则树的直径就是v->s
证明:
若能证明从任意一个点出发,bfs到的最远点一定在树的直径的端点上,那么第二次bfs就可以证明一定正确了,下面来证明第一次bfs正确性:
①若选择的点u在直径上,那么能到的最远点v一定是树的直径的端点之一
反证:
若v不是树的直径的端点,设树的直径的端点为v1,v2
那么就说明path(u,v)>path(u,v1),path(u,v)>path(u,v2)
所以path(v1,u)+path(u,v)>path(v1,v2),这说明v1,v2不是树的直径的端点,与题设不符,故"v不是树的直径的端点"不成立
②若选择的点u不在直径上,那么能到的最远点v一定是树的直径的端点之一。这个也很好说明,我们知道树的直径一定是经过根节点的(特殊的根节点如果只有一 个孩子那么就连到根节点为止),即可以理解跨过根节点两边。所以我们在第一次从u bfs的时候,中间一定会bfs到直径上的某点,那么接下来就是又回到了① 情况了。
综上,成立、
<span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
int dist[40010];//以该点为尾点的最长路径
int head[40010];//头'指针'
bool vis[40010];//标记遍历过的点
int n,m;
struct Edge//定义边的结构体
{
int from,to,val,next;
}edge[80010];//注意是无向图,边数是二倍的
int edgenum;//边数
void init()//初始化,不可少
{
edgenum=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int w)
{
edge[edgenum].from=u;//起点
edge[edgenum].to=v;//终点
edge[edgenum].val=w;//权值
edge[edgenum].next=head[u];//指向下一条边
head[u]=edgenum++;
}
int ans;//最终的最长路径
int node;//记录端点值
void bfs(int s)
{
queue<int>q;//定义队列
memset(vis,false,sizeof(vis));//初始化,清零
memset(dist,0,sizeof(dist));
q.push(s);//入列
vis[s]=true;//记录为遍历过的点
ans=0;
node=s;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=edge[i].next)//遍历每一条边
{
int v=edge[i].to;
if(!vis[v]&&dist[v]<dist[u]+edge[i].val)
{
vis[v]=true;
dist[v]=dist[u]+edge[i].val;//到v的最长路径
if(ans<dist[v])
{
ans=dist[v];//不断更新最长路径
node=v;
}
q.push(v);//重新入列,寻找下一个点
}
}
}
}
int main()
{
int a,b,c;
char h[2];
while(~scanf("%d%d",&n,&m))
{
init();
for(int i=0;i<m;i++)
{
scanf("%d%d%d%s",&a,&b,&c,&h);
addedge(a,b,c);//双向边
addedge(b,a,c);
}
bfs(1);
bfs(node);
printf("%d\n",ans);
}
return 0;
}
</span>
其中bfs还可以写成:
<span style="font-size:18px;">void bfs(int s)
{
queue<int>q;
memset(dist,0,sizeof(dist));
memset(vis,false,sizeof(vis));
q.push(s);
vis[s]=true;
ans=0;
node=s;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(!vis[v])
{
if(dist[v]<dist[u]+edge[i].val)
{
dist[v]=dist[u]+edge[i].val;
}
vis[v]=true;
q.push(v);
}
}
}
for(int i=1;i<=n;i++)//有时是0到n-1,代表所有的点
{
if(ans<dist[i])
{
ans=dist[i];
node=i;
}
}
}</span>
加边可以简写为:
void addedge(int a,int b,int c)
{
node E={a,b,c,head[a]};
edge[edgenum]=E;
head[a]=edgenum++;
}