思路:
求某个点使得它到图中的所有点的最大距离最小。
首先直接找图的直径是不对的,比如环上等距的三个点,然后其中一个点往外扩展了一个点,由于我求出来的点之后按理要放在直径的中点上,但这种情况显然不满足。
对于这种带了环的题目(又叫基环外向树),我们一般是断掉环上的某一条边,再作考虑。
把环上的某一条边断掉变成树再求直径就没问题了。
首先外送快餐店送餐的路径不会经过完整的一个环,所以断边对正确答案没有影响。因为环上的点有两个条路径,所以删掉不会导致“错过正确答案”。
先跑一遍dfs,找出环上的点编号(环上有top条边),以及点i和i+1的边的编号是i,点top和1的边编号top。
我们不知道断掉那条边最优,所以需要枚举断掉边
先断掉边top(环上边的编号,链接环上的第top个点和第1个点)
对环上每个点的子树跑一遍dfs得到dp[i]表示环上的第i个点子树的深度(同时求出所有子树内最大链长度)
然后求出表示环上的点的最大深度,last[i][0]表示经过环上的点中至少一个点的最大链长度
以及求出表示环上的点的最大深度,last[i][1]表示经过环上的点中至少一个点的最大链长度
断掉边top的最大链长度(需要经过环上的点)就是last[top][0];
断边i后的最大链(需要经过环上的点)可能是或的最大链(需要经过环上的点),也可能是和中深度最大的链相连(需要经过环上的点)。
长度最大的链可能不经过环上的点,所以最后还要和所有子树内最大链长度比较。
Code:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+7,maxm=2e5+7; int head[maxn],Next[maxm],to[maxm],fa[maxn],tot,b[maxn],top; bool vis[maxn],sccno[maxn]; ll val[maxm],pre[maxn][2],last[maxn][2],fa_w[maxn],edge[maxn],dp[maxn],chain; inline ll read() { ll s = 0, w = 1; char ch = getchar(); while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); } while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar(); return s * w; } void add(int x,int y,ll v) { to[++tot]=y; Next[tot]=head[x]; head[x]=tot; val[tot]=v; } void dfs_find(int x,int f) { vis[x]=true; for(int i=head[x],y;i;i=Next[i]) { y=to[i]; if(y==f||sccno[y]) continue; if(vis[y]) { int u=x; while(u!=y) { sccno[u]=true; b[++top]=u,edge[top]=fa_w[u]; u=fa[u]; } sccno[y]=true; b[++top]=y,edge[top]=val[i]; continue; } fa[y]=x,fa_w[y]=val[i]; dfs_find(y,x); } } void dfs(int x,int f) { for(int i=head[x],y;i;i=Next[i]) { y=to[i]; if(y==f||sccno[y]) continue; dfs(y,x); chain=max(chain,dp[x]+dp[y]+val[i]); dp[x]=max(dp[x],dp[y]+val[i]); } } int main() { int n=read(); for(int i=1,u,v,cost;i<=n;++i) { u=read(),v=read(),cost=read(); add(u,v,cost); add(v,u,cost); } dfs_find(1,0); for(int i=1;i<=top;i++) dfs(b[i],0); ll sum=0,plus=0,duan=edge[top]; for(int i=1,now;i<=top;++i) { sum+=edge[i-1]; now=b[i]; pre[i][0]=max(pre[i-1][0],dp[now]+sum); last[i][0]=max(last[i-1][0],dp[now]+plus+sum); plus=max(plus,dp[now]-sum); } edge[top]=sum=plus=0; for(int i=top,now;i;--i) { sum+=edge[i]; now=b[i]; pre[i][1]=max(pre[i+1][1],dp[now]+sum); last[i][1]=max(last[i+1][1],dp[now]+plus+sum); plus=max(plus,dp[now]-sum); } ll ans=last[top][0],choose=0; for(int i=1;i<top;++i) { choose=max(last[i][0],last[i+1][1]); choose=max(choose,pre[i][0]+pre[i+1][1]+duan); if(choose<ans) ans=choose; } ans=max(ans,chain); if(ans&1) printf("%lld.5",ans>>1); else printf("%lld.0",ans>>1); return 0; }