树的直径

树的直径一般有两种求法,一是两次dfs或bfs,另一种是树形dp

两次bfs (dfs)

具体实现上,就是进行两次搜索,第一次的时候以任意的节点为根进行遍历,找到一个距离最远的点,即为直径起点,第二次的时候以该起点为根进行搜索,再找到距离最远的点,即为直径的终点

注:两次搜索的处理方法可以有效地寻找直径的具体路径,但是无法处理负边权的情况

//dfs程序
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#define ll long long

inline ll read()
{
	ll x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch))
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch))
	{
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	return x*f;
}

const ll maxn=1e4+10;
const ll inf=1e10+10;
ll n,ans,maxx=-inf,st,en;
ll dis[maxn],vis[maxn];
std::vector<std::pair<ll,ll> > e[maxn];

inline void dfs(ll x)
{
	vis[x]=1;
	for(int i=0;i<e[x].size();i++)
	{
		ll y=e[x][i].first;
		
		if(!vis[y])
		{
			dis[y]=dis[x]+e[x][i].second;
			
			dfs(y);
		}
	}
}

int main(void)
{
	n=read();
	for(int i=1;i<=n-1;i++)
	{
		ll x=read(),y=read(),z=read();
		e[x].push_back(std::make_pair(y,z));
		e[y].push_back(std::make_pair(x,z));
	}
	
	for(int i=1;i<=n;i++) dis[i]=inf;
	std::memset(vis,0,sizeof(vis));
	
	dis[1]=0;
	dfs(1);
	
	for(int i=1;i<=n;i++)
	{
		if(dis[i]>maxx&&dis[i]!=inf)
		{
			maxx=dis[i];
			st=i;
		}
	}
	
	for(int i=1;i<=n;i++) dis[i]=inf;
	std::memset(vis,0,sizeof(vis));
	
	dis[st]=0;
	dfs(st);
	
	maxx=-inf;
	
	for(int i=1;i<=n;i++)
	{
		if(dis[i]>maxx&&dis[i]!=inf)
		{
			maxx=dis[i];
			en=i;
		}
	}
	
	printf("%lld\n",maxx);
}
//bfs程序
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#define ll long long
#define db double

const ll maxn=1e4+10;
const db inf=1e9+10;
ll n,st,en,tot;
ll ans,maxx;
ll dis[maxn],vis[maxn],pre[maxn],used[maxn],head[maxn];
std::vector<std::pair<ll,ll> > e[maxn];  
std::queue<ll> q;

struct node
{
	ll u,v,w,nxt;
} s[maxn];

inline void add(ll u,ll v,db w)
{
	s[++tot].v=v;
	s[tot].w=w;
	s[tot].nxt=head[u];
	head[u]=tot;
}

inline void bfs(ll x)
{
	pre[x]=0;
	vis[x]=1;
	q.push(x);
	
	while(q.size())
	{
		ll u=q.front();
		q.pop();
		
		for(int i=0;i<e[u].size();i++)
		{
			ll v=e[u][i].first;
			db w=e[u][i].second;
			
			if(!vis[v])
			{
				pre[v]=u;
				dis[v]=dis[u]+w;
				vis[v]=1;
				q.push(v);
			}
		}
	}
}

inline void zj()
{
	for(int i=1;i<=n;i++) dis[i]=inf;
	std::memset(vis,0,sizeof(vis));
	dis[1]=0;	
	bfs(1);
	maxx=-1;
	for(int i=1;i<=n;i++)
	{
		if(dis[i]>maxx&&dis[i]!=inf)
		{
			st=i;
			maxx=dis[i];
		}
	}
	for(int i=1;i<=n;i++) dis[i]=inf;
	memset(vis,0,sizeof(vis));
	dis[st]=0;
	bfs(st);
	maxx=-1;
	for(int i=1;i<=n;i++)
	{
		if(dis[i]>maxx&&dis[i]!=inf)
		{
			en=i;
			maxx=dis[i];
		}
	}

	ans+=maxx;
}

int main(void)
{
	scanf("%lld",&n);
	
	for(int i=1;i<=n-1;i++)
	{
		ll x,y;
		db z;
		scanf("%lld %lld %lf",&x,&y,&z);
		e[x].push_back(std::make_pair(y,z));
		e[y].push_back(std::make_pair(x,z));
	}
	
	zj();

	printf("%lld\n",ans);
	
	return 0;
}

DP求解

一条路径可看为路径中一边与该边两端点到对应直径端点的距离和,在DP中处理最大值,并维护最长链即可

注:DP可以处理存在负边权的情况,但是不能处理出直径的具体方案

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#define ll long long

inline ll read()
{
	ll x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch))
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch))
	{
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	return x*f;
}

const ll maxn=1e5+10;
ll n,ans=-maxn*maxn,tot;
ll d[maxn],vis[maxn],head[maxn];
struct node
{
	ll v,w,nxt;
} s[maxn];

inline void add(ll u,ll v,ll w)
{
	s[++tot].v=v;
	s[tot].w=w;
	s[tot].nxt=head[u];
	head[u]=tot;
}

inline void dp(ll x)
{
	vis[x]=1;
	
	for(int i=head[x];i;i=s[i].nxt)
	{
		ll y=s[i].v;
		ll w=s[i].w;
			
		if(vis[y]) continue;
		
		dp(y);
		
		ans=std::max(ans,d[x]+d[y]+w);
		d[x]=std::max(d[x],d[y]+w);
	}
}

int main(void)
{
	n=read();
	for(int i=1;i<=n-1;i++)
	{
		ll x=read(),y=read(),z=read();
		add(x,y,z);
		add(y,x,z);
	}
	
	dp(1);
	
	printf("%lld\n",ans);
	
	return 0;
}
posted @ 2020-12-01 10:55  雾隐  阅读(117)  评论(0编辑  收藏  举报