题目
Tri_integral Trainning 4
题意:
一个图,有两种点:旅店和旅客中心。要建一个机场p,对于每个旅店i,用 s(p, i)表示从机场到某个旅客中心再到这个旅店的最短距离。每个旅店有一个旅客人数t(i),要使 s(p, i)×t(i)的最大值最小。机场可以建在任意点上或边的某个位置上。
题解:
首先用floyd处理任意一个点作为机场的各 s(p, i)。然后枚举每条边二分答案:
对于二分的答案mid,任意 s(p, i)×t(i)都不能这个值,但是机场可能先经过枚举的边的u或者先经过v,所以对先经过u,可以计算出一个区间[l,r],机场建在这个范围内对旅店i都可以接受,对v同理。所以对所有的旅店,每个旅店选一个区间,只要公共部分存在,就说明存在一段区间使机场建在这个位置对所有旅店都不超过mid。
但是直接这样写会T,因为最坏情况有2^n个区间,要剪枝:如果这两个区间有交集,说明机场对这个旅店不管建在该边上哪里都可以(两个区间一个左端点一定为0,一个右端点一定为w),所以可以不更新区间。
这样虽然能过但还是很慢,再加几个:
1、如果u和v都是旅店那找不出更优解(因为一定要先经过一个旅客中心,注意是更优不是最优),所以省掉没关系。
2、先算一下建在点上最小的答案,二分的时候以这个为上限,并且二分前先算下以上限为mid是否能接受。
//Time:86ms //Memory:0KB //Length:3401B #include <iostream> #include <cstdio> #include <cstdlib> #include <string> #include <cstring> #include <algorithm> #include <vector> #include <cmath> #include <map> #include <queue> #define MAXN 301 #define MAXM 10000 #define MP(x,y) make_pair(x,y) #define FI first #define SE second #define EPS 1e-6 #define PDD pair<double,double> using namespace std; int dist[MAXN][MAXN]; int ma[MAXN][MAXN]; int uu[MAXM],vv[MAXM],ww[MAXM],th[MAXM]; double ans; inline PDD npair(PDD a,PDD b) { return MP(max(a.FI,b.FI),min(a.SE,b.SE)); } vector<PDD > check(int u,int v,int w,int h,double tans,vector<PDD >&ori,vector<PDD > &vec) { PDD inte1,inte2; if(1.0*ma[u][h]*th[h]>tans-EPS) inte1=MP(1e10,-1e10); else if(1.0*(ma[u][h]+w)*th[h]>tans-EPS) inte1=MP(0,tans/th[h]-ma[u][h]); else inte1=MP(0,w); if(1.0*ma[v][h]*th[h]>tans-EPS) inte2=MP(1e10,-1e10); else if(1.0*(ma[v][h]+w)*th[h]>tans-EPS) inte2=MP(0,tans/th[h]-ma[v][h]); else inte2=MP(0,w); inte2.FI=w-inte2.FI,inte2.SE=w-inte2.SE; swap(inte2.FI,inte2.SE); if(inte1.SE>inte2.FI-EPS) inte1=MP(0,w), inte2=MP(1e10,-1e10); for(int i=0;i<ori.size();++i) { PDD tmp; if(inte1.FI<inte1.SE+EPS) { tmp=npair(inte1,ori[i]); if(tmp.FI<tmp.SE) vec.push_back(tmp); } if(inte2.FI<inte2.SE+EPS) { tmp=npair(inte2,ori[i]); if(tmp.FI<tmp.SE) vec.push_back(tmp); } } return vec; } bool cal(double mid,int n,int u,int v,int w) { int s=0; vector<PDD > interval[2]; interval[s].push_back(MP(0,w)); for(int i=1;i<=n;++i) { interval[!s].clear(); check(u,v,w,i,mid,interval[s],interval[!s]); s=!s; if(interval[s].size()==0) return false; } return true; } double mfind(int u,int v,int w,int n) { double l=0,mid,ret=1e100,r=ans; mid=ans; if(!cal(mid,n,u,v,w)) return ret; while(l<r-EPS) { mid=(l+r)/2; if(cal(mid,n,u,v,w)) r=ret=mid; else l=mid; } return ret; } int main() { //freopen("H:\\MyDocument\\Code\\input.txt","r",stdin); int n,k,m; while(scanf("%d%d%d",&n,&k,&m)==3) { if(!n&&!k&&!m) break; memset(dist,0x3f,sizeof(dist)); memset(ma,0x3f,sizeof(ma)); for(int i=1;i<=n+k;++i) dist[i][i]=0; for(int i=0;i<m;++i) scanf("%d%d%d",&uu[i],&vv[i],&ww[i]), dist[uu[i]][vv[i]]=dist[vv[i]][uu[i]]=ww[i]; for(int l=1;l<=n+k;++l) for(int i=1;i<=n+k;++i) for(int j=1;j<=n+k;++j) dist[i][j]=min(dist[i][l]+dist[l][j],dist[i][j]); for(int l=n+1;l<=n+k;++l) for(int i=1;i<=n+k;++i) for(int j=1;j<=n;++j) ma[i][j]=min(ma[i][j],dist[i][l]+dist[l][j]); for(int i=1;i<=n;++i) scanf("%d",&th[i]); ans=1e100; for(int i=1;i<=n+k;++i) { double tmp=0; for(int j=1;j<=n;++j) tmp=max(tmp,1.0*th[j]*ma[i][j]); ans=min(ans,tmp); } for(int i=0;i<m;++i) if(uu[i]>n||vv[i]>n) ans=min(ans,mfind(uu[i],vv[i],ww[i],n)); printf("%.3f\n",ans); } return 0; }