1. 程式人生 > >【POJ2631】Roads in the North【樹的直徑】

【POJ2631】Roads in the North【樹的直徑】

題目大意:

題目連結:http://poj.org/problem?id=2631
求一棵樹的直徑。


思路:

樹的直徑模板題。

方法一:

樹形 D P DP 求輸的直徑。
考慮以 1 1

為根節點,求出 f [ i ] f[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])

那麼再考慮求出 g [ i ] g[i] 表示經過 i i 的路徑中的最大路徑和。
那麼假設在該路徑上有兩點 x x 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])
此時如果列舉 x x y y ,時間複雜度就是 O ( n 3 ) O(n^3) ,十分不理想。
其實根本沒有必要列舉 x x y y 。我們在求 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]) 。那麼如果此時我們有列舉到了 i i 的下一個子節點 x x ,那麼此時我們就有了

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

那麼久可以直接更新 g [ i ] g[i] 了!
其實可以根本不用 g [ i ] g[i] 這個陣列,直接用 a n s ans 求直徑。

  • 樹形 D P 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 之間路徑和最大的點 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;
}