题目大意:
题目链接: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])(j∈son[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])(y∈son[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求直径。
#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;
}