参看资料:
1》https://blog.csdn.net/u013480600/article/category/2133243
2》http://www.cnblogs.com/hanyulcf/archive/2010/10/23/tree_radius.html
数的直径
顾名思义,前提是“树”,即只有一个连通分量,树的直径是图中任意两点的最短距离;
求解方法
常采用两次BFS来求解树的直径:
1》从树中任意一个节点出发,利用一次BFS找到距离该节点的最远的一个节点,那么该节点是该树直径的一个端点。
2》从该端点出发,再利用深搜找到距离该点最远的一个节点,则这两点之间的距离即为该树的直径。
PS:
1》在看这一块的资料的时候,说用邻接表存储树,默默认为是什么高深的东西,就是一个二维数组。
2》学习的过程中进入一个牛角,求解的方法是先任意找到一个点,由该点找到树直径的一个端点。为什么能够确定那一点即为端点?
原理:
设起点为u,第一次BFS找到的终点v一定是树的直径的一个端点。
证明:1》如果u 是直径上的点,则v显然是直径的终点(因为如果v不是的话,则必定存在另一个点w使得u到w的距离更长,则于BFS找到了v矛盾);
2》如果u不是直径上的点,则u到v必然于树的直径相交(反证),那么交点到v 必然就是直径的后半段了所以v一定是直径的一个端点,所以从v进行BFS得到的一定是直径长度。
3》使用两次BFS的前提是“树”,这个注意一下,在看资料的时候一直犯嘀咕,如果选中的一点是一个连通分量中的,而最长的路径在另一连通分量中怎么办。
例题解析:
POJ 1985 Cow Marathon(树的直径)
【https://blog.csdn.net/u013480600/article/details/40708245】
题意:
有一个树结构, 给你树的所有边(u,v,cost), 表示u和v两点间有一条距离为cost的边. 然后问你该树上最远的两个点的距离是多少?(即树的直径)
代码实现:
/*
程序实现用的是邻接表来表示树结构.
Head[i]==j 表示与i结点连接的边组成了一条链表, 其中第j条边是这条链的头一个元素, 接着通过j可以找到剩余的(与i连接的)边.
Edge是用来表示每条边的结构.
BFS返回从s结点能走到的最远的点的编号
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=50000+5;
const int maxm=100000+5;
//有向边
struct Edge
{
Edge(){}
Edge(int to,int cost,int next):to(to),cost(cost),next(next){}
int to; //边尾部
int cost; //边距离
int next; //指向下条边
}edges[maxm];
int cnt=0; //边总数
int head[maxn];//头结点
//添加两条有向边
void AddEdge(int u,int v,int cost)
{
edges[cnt]=Edge(v,cost,head[u]);
head[u]=cnt++;
edges[cnt]=Edge(u,cost,head[v]);
head[v]=cnt++;
}
//距离
int dist[maxn];
//BFS返回从s出发能到达的最远点编号
int BFS(int s)
{
int max_dist=0;
int id=s;
queue<int> Q;
memset(dist,-1,sizeof(dist));
dist[s]=0;
Q.push(s);
while(!Q.empty())
{
int u=Q.front(); Q.pop();
if(dist[u]>max_dist)
max_dist=dist[id=u];
for(int i=head[u]; i!=-1; i=edges[i].next)
{
Edge &e=edges[i];
if(dist[e.to]==-1)
{
dist[e.to]=dist[u]+e.cost;
Q.push(e.to);
}
}
}
return id;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)==2)
{
cnt=0;
memset(head,-1,sizeof(head));
int u,v,cost;
char c;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d %c",&u,&v,&cost,&c);
AddEdge(u,v,cost);
}
printf("%d\n",dist[BFS(BFS(u))]);
}
return 0;
}
POJ 2631 Roads in the North(树的直径)
【https://blog.csdn.net/u013480600/article/details/40707623】
题意:
有一个树结构, 给你树的所有边(u,v,cost), 表示u和v两点间有一条距离为cost的边. 然后问你该树上最远的两个点的距离是多少?(即树的直径)
代码实现:
/*
程序实现用的是邻接表来表示树结构.
Head[i]==j 表示与i结点连接的边组成了一条链表, 其中第j条边是这条链的头一个元素, 接着通过j可以找到剩余的(与i连接的)边.
Edge是用来表示每条边的结构.
BFS返回从s结点能走到的最远的点的编号
*/
//C++提交才行
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=10000+5;
const int maxm=1000000+5;
//边结构
struct Edge
{
Edge(){}
Edge(int to,int cost,int next):to(to),cost(cost),next(next){}
int to;
int cost;
int next;
}edges[maxm]; //所有边
int cnt; //边总数
int head[maxn];//头结点
//添加两条有向边
void AddEdge(int u,int v,int cost)
{
edges[cnt]=Edge(v,cost,head[u]);
head[u]=cnt++;
edges[cnt]=Edge(u,cost,head[v]);
head[v]=cnt++;
}
//dist[i]表当前点到i的距离
int dist[maxn];
//BFS返回从s结点能走到的最远的点的编号
int BFS(int s)
{
int max_dist=0;//记录最远距离
int id=s; //记录最远点
queue<int> Q;
memset(dist,-1,sizeof(dist));
dist[s]=0;
Q.push(s);
while(!Q.empty())
{
int u=Q.front(); Q.pop();
if(dist[u]>max_dist)
{
max_dist=dist[u];
id=u;
}
for(int i=head[u];i!=-1;i=edges[i].next)
{
Edge &e=edges[i];
if(dist[e.to]==-1)//未访问过e.to点
{
dist[e.to]=dist[u]+e.cost;
Q.push(e.to);
}
}
}
return id;
}
int main()
{
int u,v,cost;
memset(head,-1,sizeof(head));
cnt=0;
while(scanf("%d%d%d",&u,&v,&cost)==3)
AddEdge(u,v,cost);
printf("%d\n",dist[BFS(BFS(u))]);
return 0;
}