题目描述 Description
CS有n个小区,并且任意小区之间都有两条单向道路(a到b,b到a)相连。因为最近下了很多暴雨,很多道路都被淹了,不同的道路泥泞程度不同。小A经过对近期天气和地形的科学分析,绘出了每条道路能顺利通过的时间以及这条路的长度。
现在小A在小区1,他希望能够很顺利地到达目的地小区n,请帮助小明找出一条从小区1出发到达小区n的所有路线中(总路程/总时间)最大的路线。请你告诉他这个值。
输入描述 Input Description
第一行包含一个整数n,为小区数。
接下来n*n的矩阵P,其中第i行第j个数表示从小区i到小区j的道路长度为Pi,j。第i行第i个数的元素为0,其余保证为正整数。
接下来n*n的矩阵T,第i行第j个数表示从小区i到小区j需要的时间Ti,j。第i行第i个数的元素为0,其余保证为正整数。
输出描述 Output Description
写入一个实数S,为小区1到达n的最大答案,S精确到小数点后3位。
样例输入 Sample Input
3
0 8 7
9 0 10
5 7 0
0 7 6
6 0 6
6 2 0
样例输出 Sample Output
2.125
数据范围及提示 Data Size & Hint
【数据说明】
30%的数据,n<=20
100%的数据,n<=100,p,t<=10000
首先枚举肯定会超时,所以采用二分+spfa 的方式来解决。首先二分答案,跑一遍mid,若为0,则二分右边,否则二分左边。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn1=205;
const int maxn2=200005;
int t[maxn1][maxn1];
int p[maxn1][maxn1];
int c[maxn1];
int h[maxn2];
bool b[maxn2];
double f[maxn1][maxn1];
double d[maxn2];
int n;
bool spfa(int x)
{
memset(d,-0x3f,sizeof(d));
memset(h,0,sizeof(h));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
int head=0,tail=1;
h[1]=x;
d[x]=0;
b[x]=true;
c[x]=1;
while(head<tail)
{
head++;
int x=h[head];
for(int i=1; i<=n; i++)
{
if(p[x][i]!=0&&d[x]+f[x][i]>d[i])
{
d[i]=d[x]+f[x][i];
if(!b[i])
{
tail++;
h[tail]=i;
b[i]=1;
c[i]=c[i]+1;
if(c[i]>n)
{
return true;
}
}
}
}
b[x]=false;
}
if(d[n]>0)
{
return true;
}
return false;
}
bool check(double x)
{
memset(f,0,sizeof(f));
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
f[i][j]=p[i][j]-x*t[i][j];
}
}
if(spfa(1))
{
return 1;
}
return 0;
}
int main()
{
double le=0,ri=100000;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
scanf("%d",&p[i][j]);
}
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
scanf("%d",&t[i][j]);
}
}
double eps=0.0001;
while(ri-le>eps)
{
double mid;
mid=(le+ri)/2;
if(check(mid)==1)
{
le=mid;
}
else
{
ri=mid;
}
}
printf("%.3lf",le);
return 0;
}