网络最大流

算法问题描述

网络流就是网络流,无FUCK说

最大流最小割定理

证明:
∵对于任一割(S,T),S到T的流量f必定全部从这一割经过
∴|f|<=c(S,T)
又当图中不存在增光路时,一定存在一个割(S,T)的容量被流满
即|f|=c(S,T)
∴f是F集合中最大的,c(S,T)是集合C中最小的,且f=c(S,T)
于是可以得到最小割等于最大流,且增光路算法可以找到最小割,也就可以得到最大流。

朴素FF算法

考虑贪心DFS找有残余流量的一条S->T增光路,但该方法容易找到反例。
考虑给算法反悔的方式,每次走完u->v,u-v剩余容量-=w,加一条反边v->u,其容量+=w,做同样的寻找。这可以理解为下次走x->v->u->y的时候把流量从v退到u再从u流到y,同时用x到v的流量填补v的入流也就是本来v->u的流量。

Dinic算法

算法思想

考虑朴素算法DFS找增光路每条边利用一次有点浪费,于是用BFS改进,把找增广路变成找增广网。
先用BFS给网络分层,形成一条一条可走的路径,同时判断能不能走到T
DFS找增广网,因为是网,所以到底传回来了多少流量是要记flu+=dw的!!!!!

弧优化

因为走u-v1的时候就已经把u-v1流量榨干了,下次走u直接跳到下一个v2,同时如果走完u没能把上次传下来的最小流量用完也就是在u出现了更窄的口子这次增广中也不要再走u了。算法实现上前者利用head的替身cur,后者令level_u=0。

代码
bool BFS(int s,int t){
    memset(lev,0,sizeof lev);
    for(int i=1;i<maxn;i++) cur[i]=head[i];
    lev[s]=1;
    queue<int> Q;
    Q.push(s);
    while(!Q.empty()){
        int u=Q.front(); Q.pop();
        for(int i=head[u];i!=-1;i=e[i].nxt){
            int v=e[i].v,w=e[i].w;
            if(!lev[v] && w){
                Q.push(v);
                lev[v]=lev[u]+1;
                if(v==t) return 1;
            }
        }
    }
    return 0;
}
int DFS(int u,int minw,int t){
    int flu=0;
    if(u==t || !minw) return minw;
    for(int &i=cur[u];i!=-1;i=e[i].nxt){
        int v=e[i].v,w=e[i].w;
        if(lev[v]==lev[u]+1 && w){
            int dw=DFS(v,min(minw-flu,w),t);
            e[i].w-=dw;
            e[i^1].w+=dw;
            flu+=dw;
            if(flu==minw) break;
        }
    }
    if(flu!=minw) lev[u]=0;
    return flu;
}
LL Dinic(int s,int t){
    LL ans=0;
    while(BFS(s,t)) ans+=DFS(s,INF,t);
    return ans;
}

ISAP算法

算法思想

和Dinic同样基于分层的思想,维护d[i]为结点i到结点T的最短距离,每次走d[u]==d[v]+1的边,在跑增光路的过程中动态更新d[i]的值,从而节省BFS的时间。

小优化

记num[i]为d为i的结点个数,出现断层时退出
当前弧优化照样,在走不动的时候重置

代码
void BFS(int s,int t){
    queue<int> Q;
    Q.push(t);
    d[t]=0;
    while(!Q.empty()){
        int u=Q.front(); Q.pop();
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].u;
            if(d[v] || v==t) continue; //易错点
            d[v]=d[u]+1;
            Q.push(v);
        }
    }
}
long long Augument(int s,int t){
    int u=t,minw=INF;
    while(u!=s){
        int w=edge[p[u]].w;
        minw=min(minw,w);
        u=edge[p[u]].u;
    }
    u=t;
    while(u!=s){
        edge[p[u]].w-=minw;
        edge[p[u]^1].w+=minw;
        u=edge[p[u]].u;
    }
    return minw;
}
long long ISAP(int s,int t){
    long long ans=0;
    BFS(s,t);
    for(int i=1;i<=n;i++) cur[i]=head[i];
    memset(num,0,sizeof(num));
    for(int i=1;i<=n;i++) num[d[i]]++;
    int u=s;
    while(d[s]<n){
        if(u==t){
            ans+=Augument(s,t);
            u=s;
        }
        bool flag=0;
        for(int& i=cur[u];i!=-1;i=edge[i].next){
            int v=edge[i].v,w=edge[i].w;
            if(w && d[u]==d[v]+1){
                flag=1;
                p[u=v]=i;
                break;
            }
        }
        if(!flag){
            int m=n-1;
            if(--num[d[u]]==0) break;
            for(int i=head[u];i!=-1;i=edge[i].next){
                int w=edge[i].w,v=edge[i].v;
                if(w) m=min(m,d[v]);
            }
            num[d[u]=m+1]++;
            cur[u]=head[u];
            if(u!=s) u=edge[p[u]].u; //易错点
        }
    }
    return ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jarden_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值