Codevs 1183 泥泞的道路

题目:

http://codevs.cn/problem/1183/

题解:
二分+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;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值