题目地址:http://codeforces.com/gym/100206/attachments
思路:
1.求出任意两点最短距离g(i,j),对于建在边u-v长为len的消防站(位置为x),则到点i的最短距离为min{g[u][i]+x,len-x+g[v][i]}(r<=x<=l-r)。
2.枚举每条可建消防站的边,显然总期望距离为一凹函数,三分求取最小值即可。
3.当图不连通或道路长度不足以建站时无解,当n=1且r=0时期望为0.
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug
using namespace std;
const int INF=0x3f3f3f3f;
const double eps=1e-6;
const int maxn=100+50;
struct Edge
{
int u,v,l;
};
int n,m,r;
int g[maxn][maxn];
Edge e[maxn*maxn];
int fa[maxn];
double a[maxn];
void init()
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
g[i][j]=INF;
}
}
for(int i=1; i<=n; i++) fa[i]=i,g[i][i]=0;
}
int Find(int x)
{
return fa[x]==x?x:fa[x]=Find(fa[x]);
}
void floyd()
{
for(int k=1; k<=n; k++)
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
}
}
}
double check(double pos,int u,int v,int len)
{
double sum=0.0;
for(int i=1; i<=n; i++)
{
sum+=a[i]*min(g[u][i]+pos,g[v][i]+len-pos);
}
return sum;
}
double solve(double l,double r,int u,int v,int len)
{
while(fabs(r-l)>eps)
{
double mid1=(l+r)/2.0;
double mid2=(mid1+r)/2.0;
if(check(mid1,u,v,len)<check(mid2,u,v,len))
{
r=mid1;
}
else
{
l=mid2;
}
}
return l;
}
int main()
{
#ifdef debu
freopen("in.txt","r",stdin);
#endif // debug
freopen("fire.in","r",stdin);
freopen("fire.out","w",stdout);
scanf("%d%d%d",&n,&m,&r);
init();
for(int i=1; i<=n; i++)
{
scanf("%lf",&a[i]);
}
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].l);
g[e[i].u][e[i].v]=g[e[i].v][e[i].u]=e[i].l;
int u=Find(e[i].u),v=Find(e[i].v);
if(u!=v) fa[u]=v;
}
int tmp=Find(1),flag=0;
for(int i=2; i<=n; i++)
{
if(tmp!=Find(i))
{
flag=1;
break;
}
}
if(flag)
{
printf("-1\n");
}
else
{
floyd();
double ans=1e18;
for(int i=1; i<=m; i++)
{
if(e[i].l>=2*r)
{
int l=e[i].l;
int u=e[i].u,v=e[i].v;
double pos=solve(r,e[i].l-r,u,v,l);
ans=min(ans,check(pos,u,v,l));
}
}
if(n==1&&r==0) printf("0\n");
else if(ans>=1e18) printf("-1\n");
else printf("%.5f\n",ans/10000);
}
return 0;
}