题目:
题解:
二分+spfa
(s1+s2+s3+...+si)/(t1+t2+t3+...+ti)=v;
(s1-v*t1)+(s2-v*t2)+(...)+(si-v*ti)=0;
将边权转化为s-v*t,则问题变为二分一个v使得从1 ——n路径之和为0;
当 s总-mid*t总>0 v过小,要使l=mid,反之 r=mid
要使v尽量大,则dis[n]要尽量大,故spfa求最长路,判断正环
(这不是求最长路的真正原因)
注意:
check函数的返回值问题,要仔细考虑;
精度多一位。防卡。
那么 为什么要求最长路呢?
可以知道
dis>0的点,v需要上调,即当前的速度比答案慢,那么当前的速度就是合法的。
反之,dis<0 当前答案就是不合法的;
求最长路,让当前答案的dis尽可能大,即尽可能合法,那么结果就是当前速度的最优解;
求最短路,让dis尽可能小,尽可能不合法,那么结果就是最劣解。
二分求最短路得出的结果为 最劣解仍然能够符合条件,即有一个ansv,无论它跑哪条路,速度都大于等于ansv;
显然不同于所求答案ans 使得至少一条路的速度=ans;
代码:
#include<iostream>
#include<algorithm>
#include<algorithm>
#include<queue>
#include<cstdio>
using namespace std;
const int N=205;
int s[N][N],t[N][N];
int cnt[N],n;
bool inq[N];
double dis[N];
queue<int> Q;
void init(){
while(!Q.empty()) Q.pop();
for(int i=1;i<=n+2;i++){
cnt[i]=0;
inq[i]=0;
dis[i]=-1e9+15;
}
}
bool check(double ans){
init();
dis[1]=0;
Q.push(1);
inq[1]=1;
cnt[1]++;
while(!Q.empty()){
int u=Q.front();
Q.pop();
inq[u]=0;
for(int i=1;i<=n;i++){
if(i==u) continue;
double v=s[u][i]-ans*t[u][i];
if(dis[i]<dis[u]+v){
dis[i]=dis[u]+v;
if(!inq[i]){
Q.push(i);
cnt[i]++;
inq[i]=1;
}
if(cnt[i]>=n+2) return true;
}
}
}
if(dis[n]<0) return false;
return true;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&s[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&t[i][j]);
}
}
double l=0,r=1e5+59;
while(r-l>=0.0001){
double mid=(r+l)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.3lf",l);
return 0;
}