[WIKIOI1183]泥泞的道路[二分答案+SPFA]

http://www.wikioi.com/problem/1183/

分析一下其实很简单的,转换成图论就可以做了。

这个题目是满足二分答案的性质的,因为对于一个答案a,如果a可以,那么比a小的必然也可以。

所以我们先用二分答案去枚举

但是这个二分答案需要注意精度问题,具体是什么呢?

一般二分答案是while(l <= r) if(check(mid)) l = mid + 1; else r = mid - 1;

这样的,愚蠢的我一开始就这样写了,然后精度错误balalalala的

cheeck怎么写呢,有一个p[i]是路程,对于答案ans

满足 你走的路程 / 耗费的时间 = ans

然后就可以根据这个等式去构图,以p[i][j] - t[i][j] * ans为边权,然后求最长路,如果有环这个ans肯定合法,如果没环但是dist[n] >=0 也合法

接下来贴代码

#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#include <cmath>
#include <iostream>
#define mod 9999973
#define inf 10000000
#define N 105
using namespace std;
int n , p[N][N] , t[N][N] , num[N];
bool inq[N];
double map[N][N] , d[N] , sum , sum1;
queue <int> q;
bool check(double ans)
{
    //构图
    memset(map , 0 , sizeof(map));
    memset(inq , 0 , sizeof(inq));
    while(!q.empty()) q.pop();
    for(int i = 1 ; i <= n ; ++i) d[i] = (double)-1e8 , num[i] = 0;
    for(int i = 1 ; i <= n ; ++i)
        for(int j = 1 ; j <= n ; ++j)
        {
            if(i == j) continue;
            map[i][j] = (double)p[i][j] - (double)t[i][j] * ans;
        }
    q.push(1) , inq[1] = 1;
    d[1] = 0 , ++num[1];
    while(!q.empty())
    {
        int po = q.front(); q.pop();
        inq[po] = 0;
        if(num[po] > n)
        {
            d[n] = 1e8; break; 
        }
        for(int i = 1 ; i <= n ; ++i)
        {
            if(po == i) continue;
            if(d[i] < d[po] + map[po][i])
            {
                d[i] = d[po] + map[po][i];
                if(!inq[i]) q.push(i) , inq[i] = 1 , ++num[i];
            }
        }
    }
    if(d[n] >= 0) return 1;
    return 0;
}
void work()
{
    scanf("%d", &n);
    double l = 0 , r = 0 , mid;
    for(int i = 1 ; i <= n ; ++i) for(int j = 1 ; j <= n ; ++j) scanf("%d", &p[i][j]) , sum += p[i][j];
    for(int i = 1 ; i <= n ; ++i) for(int j = 1 ; j <= n ; ++j)
    {
        scanf("%d", &t[i][j]) , sum1 += t[i][j];
        if(p[i][j]) r = max(r , (double)p[i][j] / (double)t[i][j]);
    }
    l = sum / sum1;
    mid = (l + r) / 2;
    while(1)
    {
        if(r - l <= 0.00001) {printf("%.3lf" , mid); break;}
        if(check(mid)) l = mid; else r = mid;
        mid = (l + r) / 2;
    } 
}
int main()
{
    work();
    return 0;
}
原文地址:https://www.cnblogs.com/lalawu/p/3574990.html