题目大意: 给一棵边带权的树,你可以将一条边换个位置,换完之后还得是一棵树,要求换完之后树的直径最小。
题解
做法太强了,只能想到 的做法……
考虑枚举删去哪一条边,假如删掉当前枚举的边,那么整棵树会被分成两个部分,然后我们要把这条边重新找个位置,假如我们选择了连接部分 中的 和部分 中的 ,那么这棵树的直径就是:
直径很好求,最长链也是 一下就可以了,要分两次 ,第一次求出每个点往自己子树内走的最长链,第二次利用上一次的 出来的结果,求出每个点往父亲方向走的最长链。
最后找到每个部分内 延申出去的最长链 最短 的点就是我们要链接的点了。
代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 5010
int n,ans=2147483640;
struct edge{int x,y,z,next;};
edge e[maxn<<1];
int first[maxn],len=0;
void buildroad(int x,int y,int z)
{
e[++len]=(edge){x,y,z,first[x]};
first[x]=len;
}
struct par{
int mi,ma;
par(int x,int y):mi(x),ma(y){}
void operator +=(const par &b){mi=min(mi,b.mi);ma=max(ma,b.ma);}
};
int f[maxn],from[maxn];
void dp1(int x,int fa)
{
f[x]=from[x]=0;
for(int i=first[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==fa)continue;
dp1(y,x);
if(f[y]+e[i].z>f[x])f[x]=f[y]+e[i].z,from[x]=y;
}
}
par dp2(int x,int fa,int to_fa)
{
par p(max(f[x],to_fa),f[x]+to_fa);
int ci=0;
for(int i=first[x];i;i=e[i].next)
if(e[i].y!=from[x]&&e[i].y!=fa)ci=max(ci,f[e[i].y]+e[i].z);
for(int i=first[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==fa)continue;
if(y==from[x])p+=dp2(y,x,max(ci,to_fa)+e[i].z);
else p+=dp2(y,x,max(f[x],to_fa)+e[i].z);
}
return p;
}
int main()
{
scanf("%d",&n);
for(int i=1,x,y,z;i<n;i++)
scanf("%d %d %d",&x,&y,&z),buildroad(x,y,z),buildroad(y,x,z);
for(int i=1,x,y;i<=len;i+=2)
{
x=e[i].x;y=e[i].y;
dp1(x,y);dp1(y,x);
par re1=dp2(x,y,0),re2=dp2(y,x,0);
ans=min(ans,max(re1.mi+re2.mi+e[i].z,max(re1.ma,re2.ma)));
}
printf("%d",ans);
}