这个问题挺有趣,所以锲而不舍想要知道怎么做
了解之后发现也挺直观和朴素,怎么当时就想不出来呢。。
1.求最长路
2.要移动的边肯定在最长路上
3.枚举最长路上的每条边,对于每条边u->v(权值为w),移动它的策略是把u,v两个端点接在两边子树的最长路的中间位置
4.接好后的最长路不一定是 x+w+y,还有可能是两个子树的最长路,要判断下
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 3000
int max(int aa,int bb)
{return aa>bb?aa:bb;}
int min(int aa,int bb)
{return aa<bb?aa:bb;}
struct Edge
{
int v,w;
bool flag;
Edge()
{
v=w=0;flag=true;
}
}edge[N*2];
int head[N],adj[N*2],e,n;
int path[N],dist[N],D,end_v,end_u,temp[N];
void dfs(int u,int fa)
{
if(D<dist[u])
D=dist[u],end_v=u;
for(int i=head[u];i!=-1;i=adj[i])
if(edge[i].v!=fa&&edge[i].flag)
{
dist[edge[i].v]=dist[u]+edge[i].w;
temp[edge[i].v]=i;
dfs(edge[i].v,u);
}
}
void find(int root,int pre_root)
{
memset(dist,0,sizeof(dist));
D=-1;
dfs(root,pre_root);
memset(dist,0,sizeof(dist));
D=-1;end_u=end_v;
dfs(end_u,-1);
}
int subtree_max_mid(int st,int ed,int *t,int DD)
{
int ret=0x3fffffff;
int u=ed,j;
while(u!=st)
{
ret=min(ret,max(dist[u],DD-dist[u]));
j=t[u];
u=edge[j^1].v;
}
if(ret>=0x3fffffff) //没有进入while,没有子树
return 0;
return ret;
}
void insert(int u,int v,int w)
{
edge[e].v=v;edge[e].w=w;
adj[e]=head[u];head[u]=e++;
}
int main ()
{
int test;scanf("%d",&test);
for(int k=1;k<=test;++k)
{
memset(head,-1,sizeof(head));
e=0;
scanf("%d",&n);
if(n==1)
{
printf("Case %d: 0\n",k);
continue;
}
int u,v,w;
for(int i=1;i<n;++i)
{
scanf("%d%d%d",&u,&v,&w);
insert(u,v,w);
insert(v,u,w);
}
find(1,-1);
for(int i=0;i<=n;++i)
path[i]=temp[i];
int st,ed,j;
st=end_u,ed=end_v;
u=ed;
int ans=0x3fffffff,res,x,y,d1,d2;
while(u!=st)
{
j=path[u];
edge[j^1].flag=edge[j].flag=false;
find(u,edge[j^1].v);
d1=D;
x=subtree_max_mid(end_u,end_v,temp,D);
find(edge[j^1].v,u);
d2=D;
y=subtree_max_mid(end_u,end_v,temp,D);
res=max(d1,d2);
ans=min(ans,max(res,x+edge[j].w+y));
u=edge[j^1].v;
edge[j^1].flag=edge[j].flag=true;
}
printf("Case %d: %d\n",k,ans);
}
//system("pause");
return 0;
}