【JZOJ4826】小澳的葫芦

Description

一个有向无环图,每条边有一个权值,起点为 1 ,终点为n,现在选择一条从起点到终点的路径,使得边权和与经过点数的比值最小。

Solution

首先我们添加一个新起点,连向起点,边权为0,那么题目转化成了求从新起点到终点的路径中边权和与边数的最小比值。

设经过了 k 条边,边权分别为a1,a2,,ak,那么答案就是要求 ki=1aik 最小,设答案为 ans ,那么就有: ki=1aikans
ki=1aiansk
ki=1(aians)0

于是二分答案,把边权减去答案,最短路判定即可。

谴责一下这题数据,它并不是有向无环图,他是任意的有向图。所以这里最短路就不能直接拓扑序dp。

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define rep(i,x) for(int i=last[x];i;i=next[i])
#define N 201
#define M 2010
#define eps 0.0001
using namespace std;
int to[M],next[M],last[M],num=0;
double val[M];
void link(int x,int y,double c)
{
    num++;
    to[num]=y;
    next[num]=last[x];
    last[x]=num;
    val[num]=c;
}
double f[N];
int d[N*N];
bool vis[N];
int n,m;
int jj=0;
bool check(double mid)
{
    memset(vis,0,sizeof(vis));
    fo(i,1,n) f[i]=100000000;
    d[1]=0;
    f[0]=0;
    int l=0,r=1;
    vis[0]=true;
    while(l<r)
    {
        l++;
        int x=d[l];
        rep(i,x)
        {
            int v=to[i];
            if(f[v]>f[x]+val[i]-mid)
            {
                f[v]=f[x]+val[i]-mid;
                if(!vis[v])
                {
                    vis[v]=true;
                    d[++r]=v;
                }
            }
        }
        vis[x]=false;
    }
    return f[n]>0;
}
int main()
{
    freopen("calabash.in","r",stdin);
    freopen("calabash.out","w",stdout);
    cin>>n>>m;
    int p=0;
    fo(i,1,m)
    {
        int u,v;
        double w;
        scanf("%d %d %lf",&u,&v,&w);
        link(u,v,w);
        p+=w;
    }
    link(0,1,0);
    double l=0,r=p*1.0;
    while(l+eps<r)
    {
        double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    printf("%.3lf",l);
}
posted @ 2016-11-04 20:02  sadstone  阅读(47)  评论(0编辑  收藏  举报