【SDOI2013】费用流

Alice 和 Bob在图论课程上学习了最大流和 最小费用流的相关知识。

最大流问题 :给定一张有向图 表示运输网络 ,一个源点S和一个汇点T,每条边都有最大流量。 一个合法的网络流方案必须满足: (1) 每条边的 实际流量都不超过其最大流量且非负 ;(2) 除了源点 S和汇点 T之外,对于其余所有点都满足该点总流入量 等于总流出量;而 S点的净流出量等于 T点的净流入量,这个值也即该网络流方案的总运输量。 最大流问题就是对于给定的运输网络,求总运输量最大的网络流方案。
这里写图片描述
上图 表示 了一个 最大流问题。 对于每条边 ,右边的数代表该边的最大流量, 左边的数代表在最优解中,该边的实际流量。需要注意到 ,一个最大流问题的解可能不是唯一的。

对于一张给定的 运输网络 ,Alice 先确定一个最大流 ,如果有多种解, Alice 可以任选一种; 之后 Bob在每条边上分配单位花费 (单位花费必须是非负实数), 要求所有边的单位花费之和等于 P。总费用等于每一条边 的实际流量乘以该边的单位花费。 需要注意到, Bob在分配单位花费之前,已经知道Alice 所给出的最大流方案。

现在 Alice 希望总费用尽量小,而Bob希望总费用尽量大。我们想知道, 如 果两个人都执行最优策略 ,最大流的值和总费用分别为多少。

分析

我们先分析Bob加费用的边一定要满足什么条件?
经过计算,Bob放的边一定是流量最大的!
而我们现在从Alice的角度想,就是保证最大流的同时使得流量最大的边最小。
咦,这句话好像在哪里听过,嗯没错,就是二分!
现在我们考虑如何二分判定,我们二分的是最大流量,又要求要留出最大流。
故,我们便可以二分完后截取每条边的流量:
f(u,v)=min(f(u,v),mid)
然后跑遍最大流,看看是否跑得出来。

var
    x,y,n,m,p,nu,i:longint;
    l,r,mid,z,ans:extended;
    b,las,nex:array[1..100000] of longint;
    f,re:array[1..100000] of extended;
    dis,d:array[1..1000] of longint;
function max(l,r:extended):extended;
begin
   if l<r then exit(r);exit(l);
end;
procedure insert(x,y:longint;z:extended);
begin
    inc(nu);b[nu]:=y;nex[nu]:=las[x];las[x]:=nu;f[nu]:=z;re[nu]:=z;
    inc(nu);b[nu]:=x;nex[nu]:=las[y];las[y]:=nu;f[nu]:=0;re[nu]:=0;
end;
function bfs:boolean;
var l,r,p:longint;
begin
    l:=0;r:=1;fillchar(dis,sizeof(dis),0);dis[1]:=1;d[1]:=1;
    while l<r do begin inc(l); p:=las[d[l]];
        while p<>0 do begin
        if (f[p]>0)and(dis[b[p]]=0) then begin
           inc(r);d[r]:=b[p];dis[b[p]]:=dis[d[l]]+1;
        end;p:=nex[p];end;
    end;
    exit(dis[n]<>0);
end;
function min(l,r:extended):extended;
begin
    if l<r then exit(l);exit(r);
end;
function ditch(x:longint;y:extended):extended;
var p:longint;o:extended;
begin
    if x=n then exit(y);
    p:=las[x];ditch:=0;
    while p<>0 do begin
        if (f[p]>0)and(dis[b[p]]=dis[x]+1) then begin
           o:=ditch(b[p],min(y,f[p]));
           if o>0 then begin
           f[p]:=f[p]-o;f[p xor 1]:=f[p xor 1]+o;ditch:=ditch+o;y:=y-o;end;
        end;
        p:=nex[p];
    end;
    if ditch=0 then dis[x]:=-1;
end;
function check(x:extended):boolean;
var i:longint;an:extended;
begin
    for i:=2 to nu do begin
        f[i]:=re[i];
        if f[i]>x then f[i]:=x;
    end;
    an:=0;
    while bfs do an:=an+ditch(1,maxlongint);
    exit(an=ans);
end;
begin
     readln(n,m,p);nu:=1;
     for i:=1 to m do begin
         readln(x,y,z);
         insert(x,y,z);
         r:=max(r,z);
     end;
     while bfs do
     ans:=ans+ditch(1,maxlongint);
     writeln(ans:0:0);
     l:=0;
     while r-l>0.00001 do begin
         mid:=(l+r)/2;
         if check(mid) then r:=mid else l:=mid+0.00001;
     end;
     writeln(l*p:0:4);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值