【HDU3721】枚举+最长路

题目链接: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 */
View Code
原文地址:https://www.cnblogs.com/kane0526/p/3310915.html