思路:先求出树的直径,如果k比较小,则显然在直径上走是最优的。如果k比较多的话,意味着我们要走“往返路”去访问一些结点,则很显然最优解是使得走“往返路”访问的结点的数量最少,于是我们考虑在直径上走,不够的点我们通过走往返路去访问,这样可以使得不走往返路就访问的点(直径上的点)最多,也就是走往返路访问结点最少,是最优解。
树的直径的求法:先任取一点,求离该点最远的点u,则u一定为直径的两个端点之一,然后求出离u点最远的点,二者距离便是直径。
证明见:http://www.cnblogs.com/wuyiqi/archive/2012/04/08/2437424.html
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int N = 100001; 7 int head[N]; 8 int n, m, e; 9 int node, dist; 10 11 struct Edge 12 { 13 int v, next; 14 } edge[N * 2]; 15 16 void addEdge( int u, int v ) 17 { 18 edge[e].v = v; 19 edge[e].next = head[u]; 20 head[u] = e++; 21 } 22 23 void dfs( int u, int fa, int depth ) 24 { 25 if ( depth > dist ) 26 { 27 dist = depth; 28 node = u; 29 } 30 for ( int i = head[u]; i != -1; i = edge[i].next ) 31 { 32 int v = edge[i].v; 33 if ( v == fa ) continue; 34 dfs( v, u, depth + 1 ); 35 } 36 } 37 38 int main () 39 { 40 int t; 41 scanf("%d", &t); 42 while ( t-- ) 43 { 44 scanf("%d%d", &n, &m); 45 e = 0; 46 memset( head, -1, sizeof(head) ); 47 for ( int i = 1; i < n; i++ ) 48 { 49 int u, v; 50 scanf("%d%d", &u, &v); 51 addEdge( u, v ); 52 addEdge( v, u ); 53 } 54 dist = -1; 55 dfs( 1, -1, 0 ); 56 dist = -1; 57 dfs( node, -1, 0 ); 58 while ( m-- ) 59 { 60 int k, ans; 61 scanf("%d", &k); 62 if ( k <= dist + 1 ) ans = k - 1; 63 else ans = dist + ( k - dist - 1 ) * 2; 64 printf("%d ", ans); 65 } 66 } 67 return 0; 68 }