题目大意:给定一张图,每条边上都有一个长度和时间,求从1到n经过的路径中∑length/∑time的最大值。
思路:二分答案+SPFA。假如有一解 ∑length/∑time≥ans 更优,其中ans为已经得到的一个解,则有 ∑length−ans∗∑time≥0 ,即 ∑(lengthi−ans∗timei)≥0 ,这样就转换为了,如果一个答案更优,那么对于各个边权为 lengthi−ans∗timei 的图,其到达点n最长路不小于0。当该值从正实数方向逼近0时,答案会越来越优。这时只需二分答案检验其合理性就可以了。需要说明的一点是,如果新建图中存在正环,那么这个情况是可行的,因为这样可以无限在环上跑以使到n的权值不小于0。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=105;
int n;
double ans;
int p[maxn][maxn],t[maxn][maxn];
double a[maxn][maxn];
void init()
{
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]);
}
queue<int> q;
bool vis[maxn];
double dis[maxn];
int id[maxn];
bool spfa()
{
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
a[i][j]=(double)p[i][j]-(double)t[i][j]*ans;
memset(vis,0,sizeof(vis));
memset(id,0,sizeof(id));
memset(dis,-0x3f,sizeof(dis));
while (!q.empty())
q.pop();
q.push(1);
id[1]++;
vis[1]=1;
dis[1]=0.0;
while (!q.empty())
{
int h=q.front();
q.pop();
vis[h]=0;
for (int i=1;i<=n;++i)
{
if (i==h)
continue;
if (dis[i]<dis[h]+a[h][i])
{
dis[i]=dis[h]+a[h][i];
if (!vis[i])
{
vis[i]=1;
id[i]++;
if (id[i]>n)
return true;
q.push(i);
}
}
}
}
if (dis[n]>=0)
return true;
else
return false;
}
int main()
{
init();
double l=0.0,r=10.0;
while (r-l>0.0001)
{
ans=(l+r)/2;
if (spfa())
l=ans;
else
r=ans;
}
printf("%.3f",ans);
return 0;
}