| ||||||||||
Building RoadsTime Limit: 16000/8000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 759 Accepted Submission(s): 267
Problem Description
There is a magic planet in the space. There is a magical country on the planet. There are N cities in the country. The country is magical because there are exactly N-1 magic roads between the N cities, and from each city, it is possible to visit any other city. But after the huge expansion of the country, the road system seems to be messy. The moderator decided to rebuild the road system.
As a worker, I cannot do too much things. I could just move one road to another position connecting arbitrary 2 cities using my magic, keeping its length unchanged. Of course, afterwards all the N cities have to be still connected. I wonder how to move in order to make the farthest distance between any two cities minimum. Could you solve it for me?
Input
The first line of the input is one integer T (T ≤ 10), and then T test cases follow.
Each test case begins with a line contains only one integer N (N ≤ 2500), means there are N magic cities. The cities are numbered from 0 to N-1. Following N-1 lines, each line has 3 integers a, b and c, means there is a magic road between a and b with distance c. (0 <= a, b < N, 0 < c <= 1000)
Output
For each test case, output the case number and the corresponding minimum farthest distance. See sample for detailed format.
Sample Input
Sample Output
|
题意:给一棵树,可以移动树上的一条边,但是必须保证移动之后的图还是一棵树,问如何移动才能使得移动之后的树的直径最短。
思路:调整的边肯定在树的直径上,所以可以先求出树的直径,然后枚举每条边,然后判断
只有三种情况:1.左子树的直径,2.右子树的直径,3.左子树的中点到右子树的中点(中点:树中的每一点到该点的最大值最小)
如何求子树的直径:子树的直径一定是以原来整棵树的直径的端点为端点的,所以可以先处理处每个点到端点的距离,然后dfs找出最远的点。
如何找出树的中点:中点肯定在树的直径上。
证明:假设这样一个点a不在直径上,那么它到最远距离的点,一定会和直径产生一个交点b(由直径的性质),那么a到其他点(设为x)的最大距离一定大于b到其他点(直径的端点,设为y)的最大距离。所以a一定在直径上。
证明:其实x点就是直径的端点,因为,若x不是直径的端点,那么就有ax>ay ==>bx>by==>那么y就不应该是直径的端点了。而是x。所以x一定是直径的端点。。所以ax=ab+bx,所以ax>bx。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=3000;
const int INF=1000000000;
struct node
{
int v,next,f;
}edge[maxn*2];
int n,s,t,maxid,maxdis;
int head[maxn],num,dis1[maxn],dis2[maxn],fa1[maxn],fa2[maxn],vis[maxn];
void init()
{
num=0;
memset(head,-1,sizeof(head));
}
void add_edge(int a,int b,int f)
{
edge[num].v=b;
edge[num].next=head[a];
edge[num].f=f;
head[a]=num++;
}
void bfs(int st,int dis[],int fa[])
{
queue<int> q;
maxdis=dis[st]=0;
maxid=st;
q.push(st);
memset(vis,0,sizeof(vis));
while(!q.empty())
{
int u=q.front();q.pop();
vis[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v,w=edge[i].f;
if(vis[v])continue;
dis[v]=dis[u]+w;
if(maxdis<dis[v]){maxdis=dis[v];maxid=v;}
fa[v]=u;
q.push(v);
}
}
}
void dfs(int u,int fa,int dis[])
{
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(v==fa)continue;
if(dis[v]>maxdis){maxdis=dis[v];maxid=v;}
dfs(v,u,dis);
}
}
void solve()
{
bfs(0,dis1,fa1);
s=maxid;
bfs(s,dis1,fa1);
t=maxid;
bfs(t,dis2,fa2);
int ans=INF;
for(int i=t;i!=s;i=fa1[i])
{
int maxl,maxr,midl,midr;
int len=dis2[fa1[i]]-dis2[i];
//右子树的直径
maxid=i,maxdis=dis2[i];
dfs(i,fa1[i],dis2);
//右子树的中点,中点一定在直径上
int sr=maxid;
int tmp=dis2[sr]/2,tmp1=INF;
midl=sr;
for(int j=sr;j!=t;j=fa2[j])
if(abs(dis2[j]-tmp)<tmp1){tmp1=abs(dis2[j]-tmp);midl=j;}//cout<<1;
maxl=max(dis2[sr]-dis2[midl],dis2[midl]);
//左子树的直径
maxid=fa1[i],maxdis=dis1[fa1[i]];
dfs(fa1[i],i,dis1);
//左子树的中点,中点一定在直径上
int tl=maxid;
tmp=dis1[tl]/2,tmp1=INF;
midr=tl;
for(int j=tl;j!=s;j=fa1[j])
if(abs(dis1[j]-tmp)<tmp1){tmp1=abs(dis1[j]-tmp);midr=j;}
maxr=max(dis1[tl]-dis1[midr],dis1[midr]);
ans=min(ans,max(max(dis1[tl],dis2[sr]),maxr+maxl+len));
}
printf("%d\n",ans);
}
int main()
{
int T,cas=1;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
init();
for(int i=1;i<n;i++)
{
int u,v,d;
scanf("%d%d%d",&u,&v,&d);
add_edge(u,v,d);
add_edge(v,u,d);
}
printf("Case %d: ",cas++);
solve();
}
return 0;
}