[51Nod2558] 选址

wenge

2020-02-06 15:44:54

Personal

# [题目链接](https://www.51nod.com/Challenge/Problem.html#problemId=2558) 题目大意:给你一个$n$个点,$m$条边的连通无向图,无自环重边,每条边连接两个点$u_i,v_i$并有一个边权$w_i$。现在要求在无向图里找到一个位置,这个位置既可以在一个点上,也可以在一条边的任意位置,要求这个位置到其他所有点的最短路的最大值最小。 首先考虑到这个位置可以在点上,也可以在边上,我们可以分开处理这两个问题。 若这个位置在一个点上,显然可以用floyd算法求这些点两两之间的最短路$f[i][j]$,然后更新答案。时间复杂度$O(n^3)$。 若这个位置在一条边上,不妨设这条边连接$u,v$两个点,长度为$a[u][v]$。我们可以先枚举$u-v$。这个时候之前用floyd算法求的最短路就可以派上用场。 我们不妨假设这个位置把边$u-v$分成了长度为$x$和$a[u][v]-x$的两部分,其中长度为$x$的部分与点$u$相接,另一部分则与点$v$相接。则一个点$p$到这个位置的最短路是 $$\min(f[p][u]+x,f[p][v]+[a][u][v]-x)$$ 这样我们就可以计算当$x$为定值时的结果。我们要的答案就是 $$\max_{1\leq p \leq n}(\min(f[p][u]+x,f[p][v]+[a][u][v]-x))$$ 上式的最小值。但是$x$取何值时上式取最小值呢?经过构造多组数据的验证,发现在边$u-v$一定时,上式是关于$x$的单峰函数。于是可以用三分法求解每一条边$u-v$答案的最小值。时间复杂度$O(n^3\log_{1.5}n)$。 正解代码: ```cpp //#pragma GCC optimize(2) #include <iostream> #include <cmath> #include <algorithm> #include <string> #include <cstring> #include <cstdio> #include <queue> #include <vector> #include <map> using namespace std; typedef long long ll; #define pb push_back #define clr(a,b) memset(a,b,sizeof(a)) #define PRINT(x) cout<<#x<<" = "<<x #define br cout<<"\n" ll n,m,loop; double a[205][205],f[205][205],ans=1e9; int main(){ cin>>n>>m; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ f[i][j]=1e9; } } for(int i=1;i<=m;i++){ int u,v,w; cin>>u>>v>>w; a[u][v]=a[v][u]=w; f[u][v]=f[v][u]=w; } for(int i=1;i<=n;i++)f[i][i]=0; for(int k=1;k<=n;k++){ for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ f[i][j]=min(f[i][j],f[i][k]+f[k][j]); } } } for(int i=1;i<=n;i++){ double ans1=0; for(int j=1;j<=n;j++){ ans1=max(ans1,f[j][i]); } ans=min(ans1,ans); } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(a[i][j]!=0){ double l=0,r=a[i][j],mid1,mid2; while(r-l>=1e-10){ double ansm1=0,ansm2=0; mid1=(l+r)/2.0; mid2=(mid1+r)/2.0; for(int k=1;k<=n;k++){ double x=f[k][i],y=f[k][j]; ansm1=max(ansm1,min(x+mid1,y+a[i][j]-mid1)); ansm2=max(ansm2,min(x+mid2,y+a[i][j]-mid2)); } if(ansm1<ansm2)r=mid2; else l=mid1; } double ans1=0; for(int k=1;k<=n;k++){ double x=f[k][i],y=f[k][j]; ans1=max(ans1,min(x+l,y+a[i][j]-l)); } ans=min(ans,ans1); } } } printf("%.10lf",ans); return 0; } ```