标签:树形dp,枚举,树的直径
一上来看到这个题就慌了,只想到了 $O(n^3)$ 的做法.
碰到这种题时要一步一步冷静地去分析,观察数据范围.
首先,$nleqslant 5000$,所以可以先 $O(n)$ 枚举切断哪条边.
而如果暴力枚举连哪条边的话时间就是爆炸的,不妨冷静地分类讨论一下.
当断掉这条边后,就形成了两个小树.
那么,新树的直径无外乎只有 2 种情况:两个小树中直径的较大值(只经过一棵树的点)/经过两棵树的点.
对于第一种情况,当我们断掉这条边时就是确定好的,可以直接 O(n) 算.
而对于第二种情况,既然是每棵树都要经过一些点,不妨选择每棵树中延伸长度最小的直接连上.
由于这两种情况都是互不影响的,所以可以直接取最小值.
冷静分析,分类讨论.
#include <bits/stdc++.h> using namespace std; #define setIO(s) freopen(s".in","r",stdin) const int N=5010; int n,cnt=1,ans=453533453,tmp; int hd[N],to[N<<1],nex[N<<1],dis[N<<1],f[N],g[N],maxson[N],check[N<<1],val[N<<1]; inline void add(int u,int v,int c) { nex[++cnt]=hd[u],hd[u]=cnt,to[cnt]=v,val[cnt]=c; } void dfs1(int u,int ff) { for(int i=hd[u];i;i=nex[i]) { int v=to[i]; if(v==ff||check[i]) continue; dfs1(v,u); tmp=max(tmp,f[u]+f[v]+val[i]); if(f[v]+val[i]>f[u]) g[u]=f[u],f[u]=f[v]+val[i],maxson[u]=v; else if(f[v]+val[i]>g[u]) g[u]=f[v]+val[i]; } } void dfs2(int x,int ff,int maxx) { tmp=min(tmp,max(maxx,f[x])); for(int i=hd[x];i;i=nex[i]) { int v=to[i]; if(v==ff||check[i]) continue; if(maxson[x]==v) dfs2(v,x,max(g[x]+val[i],maxx+val[i])); else dfs2(v,x,max(f[x]+val[i],maxx+val[i])); } } inline void init() { memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); memset(maxson,0,sizeof(maxson)); } int main() { // setIO("input"); int i,j; scanf("%d",&n); for(i=1;i<n;++i) { int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w),add(v,u,w); } for(i=2;i<=cnt;i+=2) { init(); int d1,d2,d3,d4,x=to[i],y=to[i^1]; check[i]=check[i^1]=1; tmp=0, dfs1(x,0), d1=tmp; tmp=0, dfs1(y,0), d2=tmp; tmp=10000000, dfs2(x,0,0), d3=tmp; tmp=10000000, dfs2(y,0,0), d4=tmp; ans=min(ans,max(max(d1,d2),d3+d4+val[i])); check[i]=check[i^1]=0; } printf("%d ",ans); return 0; }