树的直径模板~~最长路径

两次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++;
	
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值