您的位置:首页 > 其它

【原题】【noip2007 T4】树网的核(图论,最短路)

2010-11-11 18:10 405 查看
问题

描述 Description

【问题描述】
设T=(V, E, W) 是一个无圈且连通的无向图(也称为无根树),每条边带有正整数的权,我们称T为树网(treenetwork),其中V, E分别表示结点与边的集合,W表示各边长度的集合,并设T有n个结点。
路径:树网中任何两结点a,b都存在唯一的一条简单路径,用d(a,b)表示以a,b为端点的路径的长度,它是该路径上各边长度之和。我们称d(a,b)为a,b两结点间的距离。
一点v到一条路径P的距离为该点与P上的最近的结点的距离:
d(v,P)=min{d(v,u),u为路径P上的结点}。
树网的直径:树网中最长的路径称为树网的直径。对于给定的树网T,直径不一定是唯一的,但可以证明:各直径的中点(不一定恰好是某个结点,可能在某条边的内部)是唯一的,我们称该点为树网的中心。
偏心距ECC(F):树网T中距路径F最远的结点到路径F的距离, 即
ECC(F)=max{d(v,F),v∈V}.
任务:对于给定的树网T=(V, E,W)和非负整数s,求一个路径F,它是某直径上的一段路径(该路径两端均为树网中的结点),其长度不超过s(可以等于s),使偏心距ECC(F)最小。我们称这个路径为树网T=(V,E,W)的核(Core)。必要时,F可以退化为某个结点。一般来说,在上述定义下,核不一定只有一个,但最小偏心距是唯一的。
下面的图给出了树网的一个实例。图中,A-B与A-C是两条直径,长度均为20。点W是树网的中心,EF边的长度为5。如果指定s=11,则树网的核为路径DEFG(也可以取为路径DEF),偏心距为8。如果指定s=0(或s=1、s=2),则树网的核为结点F,偏心距为12。




输入格式 Input Format

输入文件包含n行:
第1行,两个正整数n和s,中间用一个空格隔开。其中n为树网结点的个数,s为树网的核的长度的上界。设结点编号依次为1, 2, ..., n。
从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2 4 7”表示连接结点2与4的边的长度为7。
所给的数据都是正确的,不必检验。

5 2
1 2 5
2 3 2
2 4 4
2 5 3

5

分析

最简单的方法——枚举。枚举每条直径,枚举直径上的每个路径,计算偏心率。

对于路径的长度,我们可以用spfa进行预处理,时间复杂度接近n2

对于偏心距我们可以得到如下的计算方法

ecc:=max{min{dist[st,i],dist[st,j]},min{dist[ed,i],dist[ed,j]}};

st是直径的起点,ed为直径终点,i为枚举的路径的一端,j为另一端.

为什么捏,我们假设存在非直径端点的一点k,如果dis[k,i]<dis[i,st]那么st为最远距离,如果dis[k,i]>dis[i,st]那么当前的直径就不是最长的路径,就和它是直径相矛盾,所以我们对于一条直径上的路径可以得到如上计算偏心距的方法

插一副图帮助理解





反思

noip考察图论不会离开最短路,都是经典算法,比如最优贸易,关键是如何根据题意改造经典算法。贪心是不错的选择,经典算法可以做预处理,可以多次使用。在考虑时一定要尽量发挥经典算法的作用

code

program liukee;
var
dis,map:array[0..300,0..300] of longint;
a:array[0..300,0..300] of longint;
v:array[0..300] of boolean;
q:array[0..1000000] of longint;
n,s,i,j,ed,st,ans,len,ecc:longint;

function max(a,b:longint):longint;
begin
if a>b then exit(a);
exit(b);
end;

function min(a,b:longint):longint;
begin
if a<b then exit(a);
exit(b);
end;

procedure init;
var
i,x,y,z:longint;
begin
readln(n,s);
for i:=1 to n-1 do
begin
read(x,y,z);
map[x,y]:=z;
map[y,x]:=z;
inc(a[x,0]);
a[x,a[x,0]]:=y;
inc(a[y,0]);
a[y,a[y,0]]:=x;
end;
end;

procedure spfa(s:longint);//求最短路
var
i,l,r,now:longint;
begin
for i:=1 to n do
dis[s,i]:=maxlongint>>1;
fillchar(v,sizeof(v),0);
fillchar(q,sizeof(q),0);
dis[s,s]:=0;
v[s]:=true;
q[1]:=s;
l:=0;r:=1;
while l<=r do
begin
inc(l);
now:=q[l];
for i:=1 to a[now,0] do
if dis[s,a[now,i]]>dis[s,now]+map[now,a[now,i]] then
begin
dis[s,a[now,i]]:=dis[s,now]+map[now,a[now,i]];
if not v[a[now,i]] then
begin
inc(r);
q[r]:=a[now,i];
v[a[now,i]]:=true;
end;
end;
v[now]:=false;
end;
end;

begin
init;
for i:=1 to n do
spfa(i);
len:=-maxlongint;//寻找直径长度
for i:=1 to n do
for j:=1 to n do
if dis[i,j]>len then
len:=dis[i,j];
ans:=maxlongint;
for st:=1 to n-1 do//枚举起点
for ed:=st+1 to n do//枚举直径终点
if dis[st,ed]=len then//找到的是直径
for i:=1 to n do//枚举路径
if dis[st,i]+dis[i,ed]=len then
for j:=1 to n do
if(dis[st,j]+dis[j,ed]=len)and(dis[i,j]<=s) then
begin
ecc:=max(min(dis[i,st],dis[j,st]),min(dis[i,ed],dis[j,ed]));//计算ecc
if ecc<ans then ans:=ecc;
end;
writeln(ans);
end.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: