【POJ2631】Roads in the North【树的直径】

题目大意:

题目链接:http://poj.org/problem?id=2631
求一棵树的直径。


思路:

树的直径模板题。

方法一:

树形 D P DP DP求输的直径。
考虑以 1 1 1为根节点,求出 f [ i ] f[i] f[i]表示从 i i i到以 i i i为根的子树的任意节点的最大路径和。那么很明显有
f [ i ] = m a x ( f [ i ] , f [ j ] + d i s [ i ] [ j ] ) ( j ∈ s o n [ i ] ) f[i]=max(f[i],f[j]+dis[i][j])(j\in son[i]) f[i]=max(f[i],f[j]+dis[i][j])(json[i])
那么再考虑求出 g [ i ] g[i] g[i]表示经过 i i i的路径中的最大路径和。
那么假设在该路径上有两点 x x x y y y,那么就有
g [ i ] = m a x ( g [ i ] , f [ x ] + d i s [ x ] [ i ] + d i s [ i ] [ y ] + f [ y ] ) g[i]=max(g[i],f[x]+dis[x][i]+dis[i][y]+f[y]) g[i]=max(g[i],f[x]+dis[x][i]+dis[i][y]+f[y])
此时如果枚举 x x x y y y,时间复杂度就是 O ( n 3 ) O(n^3) O(n3),十分不理想。
其实根本没有必要枚举 x x x y y y。我们在求 f [ i ] f[i] f[i]时,就有 f [ i ] = m a x ( f [ y ] + d i s [ i ] [ y ] ) ( y ∈ s o n [ i ] ) f[i]=max(f[y]+dis[i][y])(y\in son[i]) f[i]=max(f[y]+dis[i][y])(yson[i])。那么如果此时我们有枚举到了 i i i的下一个子节点 x x x,那么此时我们就有了

  • f [ y ] + d i s [ i ] [ y ] f[y]+dis[i][y] f[y]+dis[i][y]
  • f [ x ] f[x] f[x]
  • d i s [ x ] [ i ] dis[x][i] dis[x][i]

那么久可以直接更新 g [ i ] g[i] g[i]了!
其实可以根本不用 g [ i ] g[i] g[i]这个数组,直接用 a n s ans ans求直径。

  • 树形 D P DP DP代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=10100;
int n,x,y,z,tot,ans,f[N],head[N];

struct edge
{
	int to,dis,next;
}e[N*2];

void add(int from,int to,int dis)
{
	e[++tot].to=to;
	e[tot].dis=dis;
	e[tot].next=head[from];
	head[from]=tot;
}

void dp(int x,int fa)
{
	for (int i=head[x];~i;i=e[i].next)
	{
		int y=e[i].to;
		if (y==fa) continue;
		dp(y,x);
		ans=max(ans,f[x]+f[y]+e[i].dis);  //求直径
		f[x]=max(f[x],f[y]+e[i].dis);  //更新
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	while (scanf("%d%d%d",&x,&y,&z)==3)
	{
		add(x,y,z);
		add(y,x,z);
	}
	dp(1,0);
	printf("%d\n",ans);
	return 0;
}

方法二:搜索

我们先从 1 1 1开始搜索,可以求出一个与 1 1 1之间路径和最大的点 p p p。那么此时点p肯定是树的其中一条直径的起点,终点肯定是与p距离最远的点q,树的直径就是p到q的路径和
还是比较好理解的。在这就不过多赘述。

  • 搜索代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=10100;
int n,x,y,z,tot,p,head[N],f[N],maxn;

struct edge
{
	int next,dis,to;
}e[N*2];

void add(int from,int to,int dis)
{
	e[++tot].to=to;
	e[tot].dis=dis;
	e[tot].next=head[from];
	head[from]=tot;
}

void dfs(int x,int fa,int s)
{
	if (s>maxn)  //从起点能到达最远的点
	{
		maxn=s;
		p=x;
	}
	for (int i=head[x];~i;i=e[i].next)
	{
		int y=e[i].to;
		if (y==fa) continue;
		dfs(y,x,s+e[i].dis);
	}
	return;
}

int main()
{
	memset(head,-1,sizeof(head));
	while (scanf("%d%d%d",&x,&y,&z)==3)
	{
		add(x,y,z);
		add(y,x,z);
	}
	dfs(1,0,0);
	maxn=0;
	dfs(p,0,0);
	printf("%d\n",maxn);
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值