最小圈

Description

对于一张有向图, 求最小的圈平均值,即若一颗圈经过 k  k 个节点,那么一个圈的平均值为圈上 k  k 条边权的和除以 k k,现要求其中的最小值。

Input

第一行 2  2 个正整数,分别为 n  n  m m
以下 m  m 行,每行 3  3 个数,表示边连接的信息

Output

一行一个数,表示最小圈的值。你的答案被视为正确当且仅当与标准答案的绝对误差不超过1e51e−5

Data Constraint

20%n<=100,m<=100020%:n<=100,m<=1000
60%:n<=1000,m<=500060%:n<=1000,m<=5000
100%:n<=3000,m<=10000100%:n<=3000,m<=10000
absWi,j<=105abs(Wi,j)<=105

Sample Input

输入1:
4 5
1 2 5
2 3 5
3 1 5
2 4 3
4 1 3
输入2:
2 2
1 2 -2.9
2 1 -3.1

Sample Output

输出1:
3.666667
输出2:
-3.000000

Solution

二分答案,分数规划,将求值问题变成可行性判断问题。
然后要利用深搜版 SPFA  SPFA 或者 dfs dfs,用于判负环,来求可行性,如果是宽搜的话会被 T T
原理:
若事先把所有边加上或减去一个值,则相应地会增大或减少所有圈的平均值。假设存在一个 ans  ans 待判断,那么我们把所有边减去 ans  ans  spfa  spfa 一下,当 dis>0  dis>0 时不再走下去。若最终找到一个负环,说明 ans  ans 答案是可行的,修改上界,否则修改下界。而这最终结果是得到一个 ans ans,然后 spfa  spfa 得到一个 dis=0  dis=0 的环。

Code

const   maxn=3005;
        maxm=10005;
        e=0.000001;
var     tot,i,n,m:longint;
        l,r,mid:real;
                p:boolean;
        cost,w:array[1..maxm] of real;
        next,yy,u,v:array[1..maxm] of longint;
        g:array[1..maxn] of longint;
        dis:array[1..maxn] of real;
        vis:array[1..maxn] of boolean;
procedure make(x,y:longint;z:real);
begin
    inc(tot);
    yy[tot]:=y;
    cost[tot]:=z;
    next[tot]:=g[x];
    g[x]:=tot;
end;
procedure spfa(x:longint);
var     i,y:longint;
begin
    vis[x]:=true;
    i:=g[x];
    while i<>0 do begin
        y:=yy[i];
        if dis[x]+cost[i]<dis[y] then begin
            if vis[y] then begin
                p:=true;
                break;
            end;
            dis[y]:=dis[x]+cost[i];
            spfa(y);
            if p then break;
        end;
        i:=next[i];
    end;
    vis[x]:=false;
end;
function judge(x:real):boolean;
begin
    tot:=1;p:=false;
    fillchar(next,sizeof(next),0);
    fillchar(g,sizeof(g),0);
    fillchar(cost,sizeof(cost),0);
    fillchar(yy,sizeof(yy),0);
    fillchar(vis,sizeof(vis),false);
    fillchar(dis,sizeof(dis),0);
    for i:=1 to m do make(u[i],v[i] ,w[i]-x);
    for i:=1 to n do begin
        spfa(i);
        if p then exit(p);
    end;
    exit(false);
end;
begin
    readln(n,m);
    for i:=1 to m do readln(u[i],v[i],w[i]);
    l:=-100005;r:=100005;
    while l+e<r do begin
        mid:=(l+r)/2;
        if judge(mid) then r:=mid else l:=mid+e;
    end;
    writeln(l:0:6);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值