题意:给你一个只有一个环的图,让你选一个点(可以在边上),使得到最远点的最短路最小。
思路:
如果是一颗树的话,就是求树的直径/2。
如果是基环树的话,这道题显然不是基环树的直径,因为是到最远点的最短路的缘故。
那我们考虑最终解到其他所有点的最短路中,一定没有环,所以一定是一棵树。
所以我们枚举基环树所有可能出现的树,即枚举在环上去一条边,求树的直径。
显然最后,我们需要得到的很多解中取最小值,明显其他的不符合最短路。
当然如果基环树的直径在树中的话,答案肯定就是基环树的直径。
暴力的复杂度是o(n*n)的,但是这道题与求基环树的直径的唯一区别就是环上取的是最小值。
所有dp滑一下就好了。
考虑开始环断开一条边变成一条链,链上一个点的解一定是前缀的最大值,后缀的最大值,前后缀交的最大值取最大。
#include <bits/stdc++.h> using namespace std; #define N 100005 #define ll long long struct edge{ int to,n;ll w; };edge d[N*2]; int h[N],vis[N],q[N], tot=1,r=0; void add(int x,int y,ll w){ d[tot]={y,h[x],w};h[x]=tot++; d[tot]={x,h[y],w};h[y]=tot++; } ll ans2,ans1,sum,maa,ma1[N],ma2[N],dis[N]; int getloof(int u,int fa){ vis[u]=1; for(int i=h[u];i;i=d[i].n){ int to=d[i].to; if(to==fa)continue; if(vis[to]){ q[++r]=u;dis[r]=d[i].w; return to; } int key=getloof(to,u); if(key){ q[++r]=u;dis[r]=d[i].w; return key!=u?key:0; } } return 0; } void dfs(int u){ vis[u]=1; for(int i=h[u];i;i=d[i].n){ int to=d[i].to; if(vis[to])continue; dfs(to); if(ma1[to]+d[i].w>=ma1[u]){ ma2[u]=ma1[u]; ma1[u]=ma1[to]+d[i].w; } else ma2[u]=max(ma2[u],ma1[to]+d[i].w); } ans1=max(ans1,ma1[u]+ma2[u]); } ll dp1[N],dp2[N],dp3[N],dp4[N]; void solve(){ memset(vis,0,sizeof(vis)); for(int i=1;i<=r;i++)vis[q[i]]=1; for(int i=1;i<=r;i++)dfs(q[i]); sum=0;dp1[1]=dp3[1]=maa=ma1[q[1] ]; for(int i=2;i<=r;i++) dp1[i]=max(dp1[i-1],(sum+=dis[i])+ma1[q[i]]), dp3[i]=max(dp3[i-1],ma1[q[i]]+(maa+=dis[i])), maa=max(ma1[q[i]],maa); sum=0;dp2[r]=dp4[r]=maa=ma1[q[r]]; for(int i=r-1;i;i--) dp2[i]=max(dp2[i+1],(sum+=dis[i+1])+ma1[q[i]]), dp4[i]=max(dp4[i+1],ma1[q[i]]+(maa+=dis[i+1])), maa=max(ma1[q[i]],maa); ans2=dp3[r]; for(int i=1;i<r;i++) ans2=min(ans2,max(dp1[i]+dp2[i+1]+dis[1],max(dp3[i],dp4[i+1]))); printf("%.1f\n",max(ans1,ans2)/2.); } int main() { int n,x,y,w; cin>>n; for(int i=1;i<=n;i++){ scanf("%d%d%d",&x,&y,&w); add(x,y,w); } getloof(1,0); solve(); return 0; }