题目描述
A new city has just been built. There’re n interesting places numbered by positive numbers from 1 to n. In order to save resources, only exactly n−1 roads are built to connect these nn interesting places. Each road connects two places and it takes 1 second to travel between the endpoints of any road.
There is one person in each of the places numbered
x
1
,
x
2
…
x
k
x_1,x_2 \ldots x_k
x1,x2…xk
and they’ve decided to meet at one place to have a meal. They wonder what’s the minimal time needed for them to meet in such a place. (The time required is the maximum time for each person to get to that place.)
输入描述:
First line two positive integers, n,k - the number of places and
persons. For each the following n-1 lines, there’re two integers
a,ba,b that stand for a road connecting place aa and bb. It’s
guaranteed that these roads connected all n places.On the following
line there’re k different positive integers x 1 , x 2 … x k x_1,x_2 \ldots x_k x1,x2…xk
separated by spaces. These are the numbers of places the persons are
at.
输出描述:
A non-negative integer - the minimal time for persons to meet together.
示例1
输入
4 2 1 2 3 1 3 4 2 4
输出
2
说明
They can meet at place 1 or 3
.
备注:
1 ≤ n ≤ 1 0 5 , 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5,1≤n≤10^5 1≤n≤105,1≤n≤105
题目到手,翻译为先:
一座新城市刚刚建成。有n个有趣的地方用从1到n的正数编号。为了节省资源,只有n-1条道路可以连接这n个有趣的地方。每条道路连接两个地方,在任何道路的端点之间行驶需要1秒钟。在每个编号为 x 1 , x 2 … x k x_1,x_2 \ldots x_k x1,x2…xk的地方都有一个人 。 他们决定在一个地方见面吃饭。他们想知道在这样一个地方见面所需的最短时间是什么。(所需时间是每个人到达该地点的最长时间。)
大意可以理解为生活中的一个场景:
在n个地方分散着k个网友,每个网友分别在不同的k个地方,他们要搞线下见面会。请问他们选在哪里,可以使网友到齐的时间最短。
那么作为一个“马后炮”,我想说,这个就是把网友们的点组合成树,再求树的直径。
树的直径:
定义:
给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径边权之和。树中最远的两个节点之间的距离被称为树的直径,连接这两点的路径被称为树的最长链。后者通常也可称为直径,即直径是一个数值概念,也可代指一条路径。
求解思路:
先从任意一点P出发,找离它最远的点Q,再从点Q出发,找离它最远的点R,R到Q的距离就是是的直径。
1)当P点在直径上时,显然Q时靠P较远的一条直径的端点,而R是另一个端点,RQ就是直径。
2)当P不在直径上时:
反证法:假设PQ不是直径。
(1)当PQ与直径AB有及交点时(如下图),A-P的距离:AC+PC 小于 P-Q的距离:PC+CQ(Q离P最远),所以CQ>AC。所以QC+CB > AC+CB = AB。
这与AB为直径的题设矛盾,故不成立
(2)PQ与直径不相交:
证明方法和上面的方法相同;还是不知道的将MN看做一个节点(MN长度对PQ,AB的长度不能产生影响),就转化为上一种情况了,结论和上一种情况一样。
具体求法:
dfs,bfs,树形dp。但是我就最简单的dfs稍稍有些了解。
DFS:
#include"iostream"
#include"cstdio"
using namespace std;
const int maxn = 1e5;
struct Edge{
int Next,to,Value;
}e[maxn*2+5];
int N,M,cnt = 0,des,ans = 0,head[maxn+5]//des是离1点最远的点。
void add_E(int a,int b,int c){
e[++cnt].to = b;
e[cnt].Next = head[a];
head[a] = cnt;
e[cnt].Value = c;
}
void dfs(int x,int fa,int dis){
if(dis>ans){
ans = dis,des = x;
}
for(int i = head[x];i;i = e[i].Next){
int pp = e[i].to;
if(pp == fa)continue;
dis += e[i].Value;
dfs(pp,x,dis);
}
}
int main(){
scanf("%d%d",&N,&M);
for(int i = 1;i <= M;i++){
scanf("%d%d%d",&x,&y,&v);
add_E(x,y,v);
add_E(y,x,v);
}
dfs(1,0,0);
dfs(des,0,0);
cout << ans<<endl;
return 0;
}
本题AC代码:
#include"iostream"
#include"cstdio"
#include"cstring"
using namespace std;
const int maxn = 1e5;
struct Edge{
int Next,to;
}e[maxn*2+5];
int N,M,cnt = 0,des,ans = 0,head[maxn+5],in[maxn+5];
void add_E(int a,int b){
e[++cnt].to = b;//该条边与a连接的点
e[cnt].Next = head[a];//记录一条与a点连接的边的下标
head[a] = cnt;//保存边的下标,以便查询
}
void dfs(int x,int fa,int dis){
if(dis>ans&&in[x]){//限定树的节点
ans = dis,des = x;
}
for(int i = head[x];i;i = e[i].Next){
int pp = e[i].to;
if(pp == fa)continue;//防止成环
dfs(pp,x,dis+1);
}
}
int main(){
scanf("%d%d",&N,&M);
memset(head,0,sizeof(head));
memset(in,0,sizeof(in));
for(int i = 1;i <= N - 1;i++){
int x,y;
scanf("%d%d",&x,&y);
add_E(x,y);
add_E(y,x);
}
for(int i = 1;i <= M;i++)
{
int a;
scanf("%d",&a);
in[a] = 1;
}
dfs(1,0,0);
dfs(des,0,0);
cout << (ans+1)/2<<endl;
return 0;
}