思路:画图分析可以得出无论两个人在什么位置、怎么遍历,最后最好情况是省出了一条链只用走一次而其他的边都一定会走两次,然后就可以联想到树的直径了,如果可以只走一次树的直径,那一定是最优解。
证明:如果起点在直径上,则二人朝互相偏离的方向去遍历,则可以只走一次直径。如果起点不在直径上,则二人可以遍历完起点所在子树后(该子树每条边被访问两次),然后一起来到直径上,所以答案就是:边权和的二倍 - 树的直径。
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, s, e, dist, node; 9 10 struct Edge 11 { 12 int v, w, next; 13 } edge[N * 2]; 14 15 void addEdge( int u, int v, int w ) 16 { 17 edge[e].v = v; 18 edge[e].w = w; 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, w = edge[i].w; 33 if ( v == fa ) continue; 34 dfs( v, u, depth + w ); 35 } 36 } 37 38 int main () 39 { 40 while ( scanf("%d%d", &n, &s) != EOF ) 41 { 42 e = 0; 43 memset( head, -1, sizeof(head) ); 44 int sum = 0; 45 for ( int i = 1; i < n; i++ ) 46 { 47 int u, v, w; 48 scanf("%d%d%d", &u, &v, &w); 49 addEdge( u, v, w ); 50 addEdge( v, u, w ); 51 sum += 2 * w; 52 } 53 dist = -1; 54 dfs( s, -1, 0 ); 55 dist = -1; 56 dfs( node, -1, 0 ); 57 sum -= dist; 58 printf("%d ", sum); 59 } 60 return 0; 61 }