题意:
给你 N 个点以及 N-1 条边将所有点两两连接(树),每个点都有对应编号。现在给你k个点,你要求出这个树上的某个点使得 所给两个点到该点的时间(经过的边数)其中最大的那个为 所能得到的所有答案的最小值。
思路:
如果仅仅是两个点,那么我们用dijkstra跑一下就可以得到。但是这是k点,切要使这些点能达到的时间最小,我们就会想到树的直径这个概念。
树的直径
给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径边权之和。树中最远的两个节点之间的距离被称为树的直径,连接这两点的路径被称为树的最长链。后者通常也可称为直径。
所以我们就想尽可能的去找这k个点所对应的树的直径,(可以想象一下你有一棵树两个点在树上随机分布,你肯定希望尽量沿着主干走,而不是把多余重复的去走分枝)
所以我们就利用求树直径的方法:(1)树形dp (2) 两次dfs或者bfs (这道题选后者比较容易
所以我们就随便选一个已知点作为起点,第一次找到最远的点,再dfs第二次求得最远路径再除以2即为所求。 所以我们用spfa跑一下最远距离的点再跑回来即是最后的直径(跑最短路的算法都可以)
code:
#include<bits/stdc++.h> using namespace std; #define accept 0 const int maxm = 1e6+5; const int maxn = 1e5+5; const int inf = 0xffffff; struct edge{ int from; int to; int w; int next; }e[maxm]; int pos[maxn],ans,far; int head[maxn]; int vis[maxn]; int dist[maxn]; int n,m,top; void add(int u,int v,int w){ e[top].from = u; e[top].to = v; e[top].w = w; e[top].next = head[u]; head[u] = top++; } void spfa(int s){ queue<int> q; for(int i = 1; i <= n ; i++) dist[i] = inf; memset(vis,false,sizeof(vis)); q.push(s); dist[s] = 0; while(!q.empty()){ int u = q.front() ; q.pop(); vis[u] = false ; for(int i = head[u] ; ~i ; i = e[i].next){ int v = e[i].to; if(dist[v] > dist[u] + e[i].w){ dist[v] = dist[u] + e[i].w; if(!vis[v]){ vis[v] = true; q.push(v); } } } } ans = 0; for(int i=1;i<=m;i++){ if(ans < dist[pos[i]]){ ans = dist[pos[i]]; far = i; } } } void init(){ memset(head,-1,sizeof(head)); top =0; } int main(){ init(); scanf("%d %d",&n,&m); for(int i=1;i<n;i++){ int u,v,w; scanf("%d %d",&u,&v); add(u,v,1); add(v,u,1); } int x; for(int i=1;i<=m;i++){ scanf("%d",&pos[i]); } spfa(pos[1]); spfa(pos[far]); ans=(ans%2)?(ans/2+1):ans/2; printf("%d ",ans); return accept; }