题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3721
题意:给你一颗n个节点n-1条边的树,每条边都有一个权值,现在让你任意移动一条边然后把这条边连接到任意两个点上,最后问你怎样移动才能使树上相距最远的两个点距离最小。
思路:先求出树的最长路,然后枚举移动最长路上的所有边,移走这条边后,原树必定分为不连接的两颗子树,分别求这两颗子树的最长路,然后分别找到两颗子树最长路上靠近中点的点,把这两个点连上刚刚从母树移走的边,再求一遍母树最长路,比较所有结果取最优解即可。
注意每次枚举移动后都要把图复原然后继续枚举。
1 #pragma comment(linker, "/STACK:1024000000,1024000000") 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 #include <queue> 6 #include <algorithm> 7 using namespace std; 8 9 const int maxn=2555; 10 const int oo=0x3fffffff; 11 int visit[maxn], pre[10*maxn]; 12 int reach[10*maxn], flow[10*maxn], head[maxn], next[10*maxn]; 13 int stack[10*maxn], sa[10*maxn], sb[10*maxn]; 14 int color[10*maxn]; 15 int st, sd, ans, edge; 16 17 struct Node 18 { 19 int u, e; 20 int dis; 21 }; 22 queue<Node>q; 23 24 inline void addedge(int u, int v, int c) 25 { 26 reach[edge]=v, flow[edge]=c, next[edge]=head[u], head[u]=edge++; 27 reach[edge]=u, flow[edge]=c, next[edge]=head[v], head[v]=edge++; 28 } 29 30 void bfs(int ss,int op) 31 { 32 memset(visit,0,sizeof(visit)); 33 while(!q.empty()) q.pop(); 34 Node s, p; 35 s.u=ss, s.dis=0, s.e=-1, sd=-1, st=ss; 36 q.push(s); 37 visit[ss]=1; 38 int maxx=0, pos; 39 while(!q.empty()) 40 { 41 p=q.front(); 42 q.pop(); 43 for(int i=head[p.u]; i>=0; i=next[i]) 44 { 45 if(color[i]||color[i^1]) continue; 46 int v=reach[i], val=flow[i]; 47 s.u=v, s.dis=p.dis+val, s.e=i; 48 if(!visit[s.u]) 49 { 50 visit[s.u]=1; 51 pre[s.e]=p.e; 52 if(s.dis>maxx) 53 { 54 st=s.u; 55 maxx=s.dis; 56 sd=s.e; 57 } 58 q.push(s); 59 } 60 } 61 } 62 ++op; 63 if(op==1) bfs(st,op); 64 else ans=maxx; 65 } 66 67 int cal(int s[], int n, double len) ///找最靠近中点的点 68 { 69 int sum=0; 70 for(int i=0; i<n; i++) 71 { 72 sum+=flow[s[i]]; 73 if(sum>=len) 74 { 75 if(sum-len<=len-(sum-flow[ s[i] ])) return reach[ s[i]^1 ]; 76 else return reach[ s[i] ]; 77 } 78 } 79 } 80 81 int Solve(int n) 82 { 83 int MIN=oo; 84 memset(color,0,sizeof(color)); 85 memset(pre,-1,sizeof(pre)); 86 bfs(1,0); 87 int top=0; 88 while(sd!=-1) stack[top++]=sd,sd=pre[sd]; 89 for(int i=0; i<top; i++) ///枚举最长路上的所有边 90 { 91 int x=stack[i], na=0, nb=0; 92 color[x]=1; 93 for(int j=0; j<edge; j++) pre[j]=-1; 94 bfs(reach[x],0); 95 while(sd!=-1) sa[na++]=sd, sd=pre[sd]; 96 int u=cal(sa,na,1.0*ans/2); 97 if(!na) u=reach[x]; 98 for(int j=0; j<edge; j++) pre[j]=-1; 99 bfs(reach[x^1],0); 100 while(sd!=-1) sb[nb++]=sd, sd=pre[sd]; 101 int v=cal(sb,nb,1.0*ans/2); 102 if(!nb) v=reach[x^1]; 103 addedge(u,v,flow[x]); 104 bfs(1,0); 105 MIN=min(MIN,ans); 106 color[edge-1]=1; ///注意把新加的边移除, 107 color[x]=0; 108 } 109 return MIN; 110 } 111 112 int main() 113 { 114 int n, T, tcase=0; 115 cin >> T; 116 while(T--) 117 { 118 cin >> n; 119 edge=0; 120 memset(head,-1,sizeof(head)); 121 for(int i=1; i<n; i++) 122 { 123 int a, b, c; 124 scanf("%d%d%d",&a,&b,&c); 125 addedge(a,b,c); 126 } 127 int tmp=Solve(n); 128 printf("Case %d: %d ",++tcase,tmp); 129 } 130 } 131 /* 132 10 133 9 134 0 1 1 135 1 2 1 136 2 3 1 137 2 4 1 138 0 5 1 139 5 6 1 140 5 7 1 141 7 8 1 142 4 143 0 1 2 144 1 2 2 145 2 3 2 146 5 147 0 1 1 148 1 2 2 149 2 3 3 150 3 4 4 151 */