题意略。
思路:
这k个点应该放在这棵树的直径上,并且能连成一条路径。如果k比树的直径上的点要多,那么我们就不用把这k个点都用上,
只需要把这棵树直径上所有的点都覆盖上就行了。如果k比树的直径上的点要少,那么我们尽量使这k个点放在这个直径的中心位置。
这个我们可以简单说一下:
首先,把这k个点放在直径中央可以保证直径上的点到这段路径的最大距离最小。但是其他的枝叶呢,可以保证其他枝叶上的点到这一段路径的最大距离最小吗?
假设我为了迁就某个枝叶上的点,使得这段路径偏离了直径的中央,那么只能说明这段枝叶比原来接口处向外的那一部分直径还长,这是不可能的,
这只能说明这段枝叶是直径的一部分。
所以我们先找出直径,再寻找直径的中间位置,然后一遍dfs,确定整棵树上到这段路径的最远距离。
详见代码:
#include<bits/stdc++.h> using namespace std; const int maxn = 100005; struct edge{ int to,w; edge(int a = 0,int b = 0){ to = a,w = b; } }; int deep[maxn],fa[maxn],store[maxn],tail,n,k; vector<edge> graph[maxn]; void dfs(int node){ for(int i = 0;i < graph[node].size();++i){ edge e = graph[node][i]; int to = e.to,w = e.w; if(fa[to] == 0){ if(deep[to] == -1) deep[to] = deep[node] + w; fa[to] = node; dfs(to); } } } int main(){ scanf("%d%d",&n,&k); int from,to,w; for(int i = 0;i < n - 1;++i){ scanf("%d%d%d",&from,&to,&w); graph[from].push_back(edge(to,w)); graph[to].push_back(edge(from,w)); } memset(deep,-1,sizeof(deep)); memset(fa,0,sizeof(fa)); fa[1] = -1; deep[1] = 0; dfs(1); int ed1 = 1,ed2; for(int i = 1;i <= n;++i){ if(deep[i] > deep[ed1]) ed1 = i; } memset(deep,-1,sizeof(deep)); memset(fa,0,sizeof(fa)); fa[ed1] = -1; deep[ed1] = 0; dfs(ed1); ed2 = ed1; for(int i = 1;i <= n;++i){ if(deep[i] > deep[ed2]) ed2 = i; } int deepest = deep[ed2]; for(int i = ed2;i != -1;i = fa[i]){ store[tail++] = i; } int l,r; if(tail <= k) l = 0,r = tail - 1; else{ int L = 0; for(int i = 0;i < tail - k + 1;++i){ if(max(deepest - deep[store[i]],deep[store[i + k - 1]]) < max(deepest - deep[store[L]],deep[store[L + k - 1]])) L = i; } l = L,r = l + k - 1; } memset(deep,-1,sizeof(deep)); memset(fa,0,sizeof(fa)); fa[store[l]] = -1; for(int i = l;i <= r;++i){ deep[store[i]] = 0; } dfs(store[l]); int ans = 0; for(int i = 1;i <= n;++i) ans = max(ans,deep[i]); printf("%d ",ans); return 0; }