hdu3549 SAP+GAP

超级久没打过网络流了,感觉要完(van)。。
于是找了个练手的题目来搞一波,决定最近做几道题目巩固一波。
(%%%hzwer网络流24题)
题意很简单,就是模板来着的。。

原本我不准备写SAP的讲解的,然而当我在网上找了半天都没找到稍微漂亮一点的SAP之后我就愤然决定造福人类。
SAP+GAP+弧优化;
我们先来讲解一下SAP
SAP主要过程讲起来简单,但是实现稍微有点复杂。
从源点开始,每一次向外找是否有边可以增广,可以就往下增广①
否则就重标号②
好讲完了(个屁)
这样讲估计是网上的大部分版本,所以说大佬都太牛了,,把我们都当智商150+的人。。
从①开始讲,具体怎么往下增广呢?
设一个fa[i]表示节点i从第fa[i]条边过来,cur[i]表示i的弧。
首先要把当前点的弧更新一波(其实这就是弧优化):
fa[edge[cur[i]].y]:=cur[i];
假如当前弧是最后一条弧(i=n),就把整条增广路遍历一遍,减去最小的容量,然后继续找。
好现在来讲②:
GAP优化:设num[i]表示d[x]=i的x的个数
我们可以很显然的知道如果num[i-1]>0,num[i+1]>0,num[i]=0那么肯定没有增广路了,直接退出就好(因为此时图已经不联通)。

代码:

type node=record
        next,x,y,z,op:longint;
end;
var
        edge:Array[0..200000]of node;
        d,num,fa,cur,head:array[0..200000]of longint;
        i,j,k,p,n,m,tot,ans,x,y,z,kk,t:longint;
procedure add(x,y,z:longint);
begin
        inc(tot);
        edge[tot].x:=x;
        edge[tot].y:=y;
        edge[tot].z:=z;
        edge[tot].op:=tot+1;
        edge[tot].next:=head[x];
        head[x]:=tot;
        inc(tot);
        edge[tot].x:=y;
        edge[tot].y:=x;
        edge[tot].z:=0;
        edge[tot].op:=tot-1;
        edge[tot].next:=head[y];
        head[y]:=tot;
end;
procedure remark(x:longint);
var
        min,i:longint;
begin
        min:=n-1;
        cur[x]:=head[x];
        i:=cur[x];
        while i<>0 do
                with edge[i] do
                begin
                        if (z>0)and(d[y]<min)then min:=d[y];
                        i:=next;
                end;
        d[x]:=min+1;
end;
procedure change;
var
        i,min:longint;
begin
        i:=n;
        min:=maxlongint;
        while i>1 do
        with edge[fa[i]] do
        begin
                if z<min then min:=z;
                i:=x;
        end;
        ans:=ans+min;
        i:=n;
        while i>1 do
        with edge[fa[i]] do
        begin
                dec(z,min);
                inc(edge[op].z,min);
                i:=x;
        end;
end;
procedure main;
var
        i,j,k:longint;
begin
        for i:=1 to n do
        cur[i]:=head[i];
        num[0]:=n;
        ans:=0;
        i:=1;
        while d[1]<n do
        begin
                while cur[i]>0 do
                        with edge[cur[i]] do
                        if(z>0)and(d[y]+1=d[x])then break
                        else cur[i]:=next;
                if cur[i]=0 then
                begin
                        dec(num[d[i]]);
                        if num[d[i]]=0 then break;
                        remark(i);
                        inc(num[d[i]]);
                        if i<>1 then i:=edge[fa[i]].x;
                end
                else begin
                        fa[edge[cur[i]].y]:=cur[i];
                        i:=edge[cur[i]].y;
                        if i=n then
                        begin
                                change;
                                i:=1;
                        end;
                end;
        end;
end;
begin
        readln(t);
        for kk:=1 to t do
        begin
                tot:=0;
                fillchar(d,sizeof(d),0);
                fillchar(edge,sizeof(edge),0);
                fillchar(fa,sizeof(fa),0);
                fillchar(num,sizeof(num),0);
                fillchar(head,sizeof(head),0);
                readln(n,m);
                for i:=1 to m do
                begin
                        readln(x,y,z);
                        add(x,y,z);
                end;
                ans:=0;
                main;
                writeln('Case ',kk,': ',ans);
        end;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值