codevs1183 泥泞的道路

题目描述 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

分析:
一开始以为是一道dp
想起了这道题

但是这个t的范围好大
枚举不就jj了吗
那怎么办呢

tag上说是二分

第一反应,二分时间
看在mid的时间内能够走到哪里
能到达n的话,就统计一下答案,r=mid
不能的话l=mid

但是我们不能保证这样就具有单调性啊(二分的要求就是答案具有单调性)
况且二分t的范围不也是很大吗,zz

那我们还能二分什么呢
只能二分最大值了

那问题就在如何判定可行性上
(s1+s2+s3)/(t1+t2+t3)=ans
难道我们要走一步看一步嘛
太不现实了

想到这里,就体现了oi们必须具备的一个重要思想

画柿子!

(s1+s2+s3)=ans*(t1+t2+t3)
s1+s2+s3=ans*t1+ans*r2+ans*t3
(s1-ans*t1)+(s2-ans*t2)+(s3-ans*t3)=0

这样一看,ans就和我们经过的每一条边都有直接关系了
我们可以把边权变成s-ans*t
每次跑一个最长路
看一看答案是不是>0
是则l=mid
否则r=mid

tip

每一种元素都要尝试一下,
熟练掌握画柿子的本领

等我第一遍写完之后,怎么也跑不出来答案,
后来看前辈在spfa的时候有一个特判

ti[way[i].y]++;
if (ti[way[i].y]>=n) return 1;

这个是判断环的
ti表示每个元素的入队次数,如果一个点的入队次数大于等于n,那么ta一定在一个环上,如果存在正环,那么一定符合条件

这里写代码片
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>

using namespace std;

const int N=102;
const double eps=1e-4;
const double INF=1e7;
int mp[N][N],t[N][N];
int n,sum=0;
struct node{
    int x,y,nxt;
    double v;
};
node way[2*N*N];
int st[N],tot;
double dis[N];
int q[N*N],tou,wei,ti[N];
bool p[N];

int dcmp(double x)
{
    if (x>=eps) return 1;
    else return 0;
}

void add(int u,int w,double z)
{
    tot++;
    way[tot].x=u;way[tot].y=w;way[tot].v=z;way[tot].nxt=st[u];st[u]=tot;
}

int spfa()
{
    for (int i=1;i<=n;i++) p[i]=1,dis[i]=-INF;
    memset(ti,0,sizeof(ti));
    tou=wei=0;
    q[++wei]=1;
    dis[1]=0.0;p[1]=0;ti[1]=1;
    do
    {
        int r=q[++tou];
        for (int i=st[r];i;i=way[i].nxt)
        {
            if (way[i].v+dis[r]>dis[way[i].y])
            {
                dis[way[i].y]=way[i].v+dis[r];
                if (p[way[i].y])
                {
                    p[way[i].y]=0;
                    q[++wei]=way[i].y;
                    ti[way[i].y]++;
                    if (ti[way[i].y]>=n) return 1;
                }
            }
        }
        p[r]=1;
    }
    while (tou<wei);
    if (dis[n]>=0) return 1;
    else return 0;
}

int pd(double mid)
{
    memset(st,0,sizeof(st)); 
    tot=0;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
        if (i!=j)
        {
            double x=((double)mp[i][j]-mid*(double)t[i][j]);
            add(i,j,x);
        }
    return spfa();
}

void erfen()
{
    double ans=0;
    double l=0,r=(double)sum;
    while (dcmp(r-l))
    {
        double mid=(l+r)/2;
        if (pd(mid))
           ans=max(ans,mid),l=mid;
        else r=mid;
    }
    printf("%0.3lf",ans);
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            scanf("%d",&mp[i][j]),sum+=mp[i][j];
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            scanf("%d",&t[i][j]);
    erfen();
    return 0;
}
原文地址:https://www.cnblogs.com/wutongtong3117/p/7673187.html