网络流学习记录

网络流学习记录

总述:

目前已学习的内容:

Ford-Fulkerson O(FE)

Edmonds-Karp O(E2V)

Dinic O(EV2)

持续学习中…

Ford-Fulkerson

最大流的一种最基础的算法,想要搞清楚首先需要弄清楚反向边的作用。

img

假设我们已经获得了一条按如图所示流向的最大流(黑色路径),此时我们该如何调整?我们发现,此时蓝色路径(通过一条反向边)中可以再添加流量,于是这些流量就可以加到答案里,直到无法再添加流量。这就是算法的运行过程。

然后,我们来考虑为什么这样是对的。通过反向边添加流量,就相当于把原来流经这条路径的流给推了回去。观察这幅图,我们推回去的流顺着蓝色箭头向汇点传输(这也就是多出来的流),而下方蓝色箭头的流则顺着原来的黑色路径传输,没有问题。这就是反向边的作用:用来撤销我们之前有可能错误的决策。

于是这就有一个很显然的算法,一直dfs,直到无法添加流量,这也就是Ford-Fulkerson算法的原理。

#define mp(a,b,c) (Edge){a,b,c}
#define ll long long
#define INF 2000000005
using namespace std;
struct Edge{int to,c,rev;};
vector<Edge>G[M];
struct Ford_Fulkerson{
    bool mark[M];
    void Addedge(int s,int t,int c){
        G[s].push_back(mp(t,c,G[t].size()));
        G[t].push_back(mp(s,0,G[s].size()-1));
    }
    int dfs(int s,int t,int f){
        if(s==t)return f;
        mark[s]=true;
        for(int i=0;i<G[s].size();i++){
            int to=G[s][i].to;
            if(!mark[to]&&G[s][i].c>0){
                int d=dfs(to,t,min(f,G[s][i].c));
                if(d>0){
                    G[s][i].c-=d;
                    G[to][G[s][i].rev].c+=d;
                    return d;
                }
            }
        }
        return 0;
    }
    int maxflow(int s,int t){
        int flow=0;
        while(1){
            memset(mark,0,sizeof(mark));
            int f=dfs(s,t,INF);
            if(f==0)return flow;
            flow+=f;
        }
    }
}Ford_Fulkerson;
Edmonds-Karp

然而dfs比较慢,所以用bfs,这就是EK算法,本质上与FF是差不多的,但是快很多(说起来好像有多快一样)。

struct Node{int to,id;}Pre[M];
int EdmondsKarp(int s,int t){
    int maxflow=0;
    while(1){
        queue<int>Q;
        for(int i=1;i<=m;i++)
            Pre[i].to=Pre[i].id=0;
        Q.push(s);
        int f=INF;
        while(!Q.empty()){
            int top=Q.front();Q.pop();
            for(int i=0;i<G[top].size();i++){
                int to=G[top][i].to;
                if(Pre[to].to||G[top][i].c==0)continue;
                Pre[to].to=top;Pre[to].id=i;
                if(to==t)break;
                Q.push(to);
            }
        }
        if(!Pre[t].to)return maxflow;
        for(int i=t;i!=s;i=Pre[i].to)
            if(G[Pre[i].to][Pre[i].id].c<f)f=G[Pre[i].to][Pre[i].id].c;
        for(int i=t;i!=s;i=Pre[i].to){
            G[Pre[i].to][Pre[i].id].c-=f;
            G[i][G[Pre[i].to][Pre[i].id].rev].c+=f;
        }
        maxflow+=f;
    }
}
Dinic

然后是重要的Dinic算法。它是对于EK算法的优化。为了防止每次bfs多次走经不可能的路段(向队列中加入无法到达的点进行拓展),它采用bfs+dfs分层图的设计。先是bfs找出一条能增广的路径,按此路径将各点分层。再使用dfs按照层次顺序寻找增广路径,进行增广。如果不能找到,则重新bfs构建分层图,直到bfs找不到一条路径。这种算法有效地避免了重复的搜索,降低了复杂度,是一种较为高效的最大流算法。

struct Dinic{
    bool mark[M];
    int id[M],level[M];
    void BFS(int s){
        queue<int>Q;
        level[s]=0;
        Q.push(s);
        while(!Q.empty()){
            int top=Q.front();Q.pop();
            for(int i=0;i<G[top].size();i++){
                int to=G[top][i].to;
                if(G[top][i].c==0||level[to]!=-1)continue;
                level[to]=level[top]+1;
                Q.push(to);
            }
        }
    }
    int dfs(int s,int t,int f){
        if(s==t)return f;
        for(int &i=id[s];i<G[s].size();i++){
            int to=G[s][i].to;
            if(level[to]>level[s]&&G[s][i].c){
                int d=dfs(to,t,min(f,G[s][i].c));
                if(d){
                    G[s][i].c-=d;
                    G[to][G[s][i].rev].c+=d;
                    return d;
                }
            }
        }
        return 0;
    }
    int maxflow(int s,int t){
        int maxflow=0;
        while(1){
            memset(level,-1,sizeof(level));
            memset(id,0,sizeof(id));
            BFS(s);
            if(level[t]==-1)return maxflow;
            int f;
            while(f=dfs(s,t,INF))maxflow+=f;
        }
    }
}Dinic;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值