题目描述
设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)=min{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。
路径:树网中任何两结点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)=min{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。
输入描述:
第1行,两个正整数n和s,中间用一个空格隔开。其中n为树网结点的个数,s为树网的核的长度的上界。设结点编号依次为1, 2, ..., n。
从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2 4 7”表示连接结点2与4的边的长度为7。
所给的数据都是正确的,不必检验。
输出描述:
输出一个非负整数,为指定意义下的最小偏心距。
示例1
输入
5 2
1 2 5
2 3 2
2 4 4
2 5 3
输出
5
示例2
输入
8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3
输出
5
备注
40%的数据满足:5 ≤ n ≤ 15
70%的数据满足:5 ≤ n ≤ 80
100%的数据满足:5 ≤ n ≤ 300, 0 ≤ s ≤ 1000。边长度为不超过1000的正整数
解答
楼上的大佬似乎都用的dfs,像我这种蒟蒻只会用暴力的Floyd算法。不过,看到这道题规模只有300,Floyd算法不会有问题。
首先用Floyd算法预处理点对之间的距离,接下来穷举每一对点构成的路径(假装每一条路径就是树网的核),如果路径长<=s,则计算其它点到该路径的最大距离,取穷举到的所有路径中最小的那个,即为答案。此时,对应的路径是最优解,也必然是树网的核。
其中,计算点到路径的距离,由于这是一张树网,且已经预处理点对之间的距离,从而点k到路径(i,j)的距离即为
首先用Floyd算法预处理点对之间的距离,接下来穷举每一对点构成的路径(假装每一条路径就是树网的核),如果路径长<=s,则计算其它点到该路径的最大距离,取穷举到的所有路径中最小的那个,即为答案。此时,对应的路径是最优解,也必然是树网的核。
其中,计算点到路径的距离,由于这是一张树网,且已经预处理点对之间的距离,从而点k到路径(i,j)的距离即为
(dist[k][i]+dist[k][j]-dist[i][j])/2
(事实上相当于把重复计算的一部分减掉后,由于剩下的部分相当于算了两次,就除以2。画个图很好理解的。)
注意树网的核可能退化为一个点,因此在穷举点对(i,j)时不要忽略i==j的情况。
下面贴上代码。
#include <bits/stdc++.h> #define F(i) for(int i=1; i<=n; ++i) //运用宏简化代码 const int N=305,INF=0x3f3f3f3f; int n,s,l,y,x,ans=INF,g[N][N]; inline void read(int &x){ //输入优化 char c; //虽然本题数据规模 while(!isdigit(c=getchar())); x=c-48; //不需要输入优化 while(isdigit(c=getchar()))x=x*10+c-48; } inline int _min(int x,int y){return x<y?x:y;} inline int _max(int x,int y){return x>y?x:y;} int main(){ read(n),read(s); memset(g,INF,sizeof g); //距离初值设为INF for(int i=1; i<n; ++i) //存图 read(x),read(y),read(l), g[x][y]=g[y][x]=l; F(k) F(i) if(i!=k) F(j) if(i!=j && j!=k) //标准的Floyd算法 g[i][j]=_min(g[i][j],g[i][k]+g[k][j]); //求点对间最短距离 F(i) F(j) if(i==j || g[i][j]<=s){ //穷举路径i->j g[i][i]=0; //需要注意的是树的 int dist=0; //核可能退化成一点 F(k) if(k!=i && k!=j) //查找偏心距 dist=_max(dist,(g[k][i]+g[k][j]-g[i][j])/2); ans=_min(ans,dist); //更新最小值 } printf("%d\n",ans); }
来源:UNIDY