题目大意
给出一副带权无向图,求图的半径,中点可以在边上。
解题思路
先求出两两最短路,枚举一条边,考虑最短路从哪一端走的临界点,把所有边的临界点排序,发现走左端的点逐渐减少,右端的点逐渐增多,可以排序后o(n)维护左端最大和右端最大,从而算出经过当前边的直径,取最大值即答案。
code
#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
#define LD double
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a>b)?b:a)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const inf=1e9,maxn=200,maxm=2*1e4,mod1=1e9+7,mod2=998244353,size=29989;
int n,m,u[maxm+10],v[maxm+10],w[maxm+10],mxx[maxn+10];
LD dis[maxn+10][maxn+10];
struct rec{
int a,b;
};
rec a[maxn+10];
bool cmp(rec x,rec y){
return x.a<y.a;
}
multiset<int>s;
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,n+1)fo(j,1,n+1)dis[i][j]=inf;
fo(i,1,m){
scanf("%d%d%d",&u[i],&v[i],&w[i]);
dis[u[i]][v[i]]=dis[v[i]][u[i]]=min(dis[u[i]][v[i]],w[i]);
}
fo(i,1,n+1)dis[i][i]=0;
fo(k,1,n)
fo(i,1,n)
fo(j,1,n)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
LD ans=inf;
fo(i,1,m){
int cnt=0;
fo(j,1,n){
a[++cnt].a=(dis[j][v[i]]-dis[j][u[i]]+w[i])/2.0;
a[cnt].b=j;
}
sort(a+1,a+cnt+1,cmp);a[cnt+1].a=w[i];int mx=0;mxx[cnt+1]=0;
fd(j,cnt,1){
mxx[j]=max(mxx[j+1],dis[a[j].b][u[i]]);
}
fo(j,1,cnt+1){
LD tmp;
if(j!=cnt+1)tmp=(mx-mxx[j]+w[i])/2.0;
else tmp=0;
if((a[j-1].a<=tmp)&&(tmp<=a[j].a))ans=min(ans,mx+w[i]-tmp);
if(j!=cnt+1)mx=max(mx,dis[a[j].b][v[i]]);
}
}
printf("%.2lf",ans);
return 0;
}