zoukankan      html  css  js  c++  java
  • 网络流入门总结(EK算法)

    终于真正意义上接触网络流了,记得第一次看网络流是在去年去区域赛的火车上,但是拿着LRJ的白书,看了好久,感觉可以了,然并卵,比赛的时候就签了一下到,就走了。。。从那以后就没碰过网络流,原来等着跟大工大神学习的,好像因为各种事情一直没有上这一课。然后又把白皮看了一遍,看了很多博客,感觉其中有一个人的博客写的特别好,现在找不到了。。。

    跟着专题训练写了几道模板题,照着大白书稍微改了一点,成为了自己的模板。下面做一些入门网络流的总结:

    1:反向边。每一条路径都对应着一条反向边,u->v流过f的流量,那个对应的反向边v->u流过-f的流量。而对应的反向边的容量cap = 0。

    2:残量网络。就是把每一条路径剩余容量标称着条边的权值。(当然反向边也是必须要算的,这个时候要注意边的数量是原来边数量的两倍,权值>0才能形成一条边)

    3:增广路。如果在残余网络中有一条路径能够从s(源点)走到t(汇点)那么,我们就称这条路为增广路。

    4:增广路求最大流。增广路对应的每一条路径权值的最小值(d)全部加到原网络时,这个时候我们发现总的流量增加了d,(注意在d添加到原边时,对应的反向边的流量需要-d,因为是对应的关系,很好理解)。那么问题就转化成如何找到所有的增广路。这个就很显然了,我们只要遍历一遍残量网络图,就可以找到了。(BFS实现,DFS比BFS慢一点,有一个人的博客有测试的)

    下面就说一个这6道模板题:

    POJ_1273  http://poj.org/problem?id=1273 很裸的最大流题,正好可以检测模板的正确性:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #define INF (1<<30)
    #define ll long long
    #define FOR(i,x,y)  for(int i = x;i < y;i ++)
    #define IFOR(i,x,y) for(int i = x;i > y;i --)
    #define N 210
    using namespace std;
    
    struct Edge{
        int from,to,cap,flow;
        Edge(){};
        Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
    }edges[N<<1];
    
    int edge_cnt;
    vector <int> G[N]; //存图
    int pre[N],a[N]; //记录增广路路径
    int s,t,n,m;
    bool vis[N];
    
    void AddEdge(int from,int to,int cap){
        edges[edge_cnt++] = Edge(from,to,cap,0);
        edges[edge_cnt++] = Edge(to,from,0,0);
        G[from].push_back(edge_cnt - 2);
        G[to].push_back(edge_cnt -1);
    }
    
    int bfs(){
        memset(vis,false,sizeof(vis));
        queue <int> q;
        q.push(s);
        a[s] = INF;
        while(!q.empty()){
            int u = q.front(); q.pop();
            if(u == t)  break;
            FOR(i,0,G[u].size()){
                int e = G[u][i];
                int v = edges[e].to;
                if(!vis[v] && edges[e].cap > edges[e].flow){
                    vis[v] = true;
                    pre[v] = e;
                    a[v] = min(a[u],edges[e].cap-edges[e].flow);
                    q.push(v);
                }
            }
        }
        if(!vis[t]) return -1;
        return a[t];
    }
    
    int EK(){
        int max_flow = 0;
        int tem;
        while((tem = bfs()) != -1){
            max_flow += tem;
            for(int u = t; u != s; u = edges[pre[u]].from){
                edges[pre[u]].flow += tem;
                edges[pre[u]^1].flow -= tem;
            }
        }
        return max_flow;
    }
    
    int main()
    {
        //freopen("test.in","r",stdin);
        while(~scanf("%d%d",&n,&m)){
            FOR(i,0,N)  G[i].clear();
            s = 1; t = m;
            int u,v,c;
            edge_cnt = 0;
            FOR(i,0,n){
                scanf("%d%d%d",&u,&v,&c);
                AddEdge(u,v,c);
            }
            printf("%d
    ",EK());
        }
        return 0;
    }

    HDU_3549  http://acm.hdu.edu.cn/showproblem.php?pid=3549 也是一个模板练手题

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #define INF (1<<30)
    #define ll long long
    #define FOR(i,x,y)  for(int i = x;i < y;i ++)
    #define IFOR(i,x,y) for(int i = x;i > y;i --)
    #define N 1010
    using namespace std;
    
    struct Edge{
        int from,to,cap,flow;
        Edge(){};
        Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
    }edges[N<<1];
    
    int edge_cnt;
    vector <int> G[N]; //存图
    int pre[N],a[N]; //记录增广路路径
    int s,t,n,m;
    bool vis[N];
    
    void AddEdge(int from,int to,int cap){
        edges[edge_cnt++] = Edge(from,to,cap,0);
        edges[edge_cnt++] = Edge(to,from,0,0);
        G[from].push_back(edge_cnt - 2);
        G[to].push_back(edge_cnt -1);
    }
    
    int bfs(){
        memset(vis,false,sizeof(vis));
        queue <int> q;
        q.push(s);
        a[s] = INF;
        while(!q.empty()){
            int u = q.front(); q.pop();
            if(u == t)  break;
            FOR(i,0,G[u].size()){
                int e = G[u][i];
                int v = edges[e].to;
                if(!vis[v] && edges[e].cap > edges[e].flow){
                    vis[v] = true;
                    pre[v] = e;
                    a[v] = min(a[u],edges[e].cap-edges[e].flow);
                    q.push(v);
                }
            }
        }
        if(!vis[t]) return -1;
        return a[t];
    }
    
    int EK(){
        int max_flow = 0;
        int tem;
        while((tem = bfs()) != -1){
            max_flow += tem;
            for(int u = t; u != s; u = edges[pre[u]].from){
                edges[pre[u]].flow += tem;
                edges[pre[u]^1].flow -= tem;
            }
        }
        return max_flow;
    }
    
    int main()
    {
        //freopen("test.in","r",stdin);
        int T,tCase = 0;
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);
            FOR(i,0,N)  G[i].clear();
            s = 1; t = n;
            int u,v,c;
            edge_cnt = 0;
            FOR(i,0,m){
                scanf("%d%d%d",&u,&v,&c);
                AddEdge(u,v,c);
            }
            printf("Case %d: ",++tCase);
            printf("%d
    ",EK());
        }
        return 0;
    }

    POJ_1087 http://poj.org/problem?id=1087 这个题注意一下对应关系就好了,题目是插头可以变成别的种类的,不是插座可以变成别的种类的,我用的map来对应的节点,建图的时候麻烦一点。(其实也可以用二分图的匹配来写,本来二分图就可以用网络流来写的,所以不影响)在插头一边加源点,插座一边加汇点,转换器对应的路径,cap=1。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #include <string>
    #include <map>
    #define INF (1<<30)
    #define ll long long
    #define FOR(i,x,y)  for(int i = x;i < y;i ++)
    #define IFOR(i,x,y) for(int i = x;i > y;i --)
    #define N 440
    
    using namespace std;
    
    struct Str{
        char s[30];
        Str() {}
        Str(char* ths) {FOR(i,0,30) s[i] = ths[i];}
        bool operator < (const Str& rhs) const{
            return strcmp(s,rhs.s) == -1 ? true : false;
        }
    }str[N];
    
    int n,m,k;
    char src[N][30],tar[N][30],sr[N][30],ta[N][30];
    int cnt,co;
    int l[N],r[N];
    bool mat[N][N];
    bool vis[N];
    vector <int> GP[N];
    map <string,int> p;
    
    void dfs(int u){
        FOR(i,0,GP[u].size()){
            int v = GP[u][i];
            if(!vis[v]) {vis[v] = true;dfs(v);}
        }
    }
    
    ///ÍøÂçÁ÷Ïà¹Ø
    int s,t,edge_cnt;
    int a[N];
    
    struct Edge{
        int from,to,cap,flow;
        Edge() {}
        Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
    }edge[N*N*2];
    
    vector <int> G[N];
    int pre[N];
    
    void AddEdge(int from,int to,int cap){
        edge[edge_cnt++] = Edge(from,to,cap,0);
        edge[edge_cnt++] = Edge(to,from,0,0);
        G[from].push_back(edge_cnt-2);
        G[to].push_back(edge_cnt-1);
    }
    
    int bfs(){
        memset(vis,false,sizeof(vis));
        queue <int> q;
        a[s] = INF;
        q.push(s);
        vis[s] = true;
        while(!q.empty()){
            int u = q.front(); q.pop();
            if(u == t)  break;
            FOR(i,0,G[u].size()){
                int e = G[u][i];
                int v = edge[e].to;
                if(!vis[v] && edge[e].cap > edge[e].flow){
                    a[v] = min(a[u],edge[e].cap-edge[e].flow);
                    pre[v] = e;
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
        if(!vis[t]) return -1;
        return a[t];
    }
    
    int EK(){
        int max_flow = 0;
        int d;
        while((d = bfs()) != -1){
            max_flow += d;
            for(int u = t;u != s;u = edge[pre[u]].from){
                edge[pre[u]].flow += d;
                edge[pre[u]^1].flow -= d;
            }
        }
        return max_flow;
    }
    
    /****/
    
    void init(){
        sort(str,str+co);
        cnt = 0;
        p[str[0].s] = (++cnt);
        FOR(i,1,co){
            if(strcmp(str[i].s,str[i-1].s) != 0) p[str[i].s] = (++cnt);
        }
        FOR(i,0,N)  GP[i].clear();
        FOR(i,0,k){
            GP[p[sr[i]]].push_back(p[ta[i]]);
            //printf("%d %d
    ",p[sr[i]],p[ta[i]]);
            //printf("%d %d
    ",p[sr[i]],GP[p[sr[i]]].size());
        }
        memset(mat,false,sizeof(mat));
        FOR(i,1,cnt+1){
            memset(vis,false,sizeof(vis));
            vis[i] = true;
            dfs(i);
            FOR(j,1,cnt+1){
                if(vis[j]) mat[i][j] = true;
            }
        }
        FOR(i,n+1,n+m+1){
            l[i] = p[tar[i]];
        }
        FOR(i,1,n+1){
            r[i] = p[src[i]];
        }
        s = 0;
        t = n+m+1;
        FOR(i,0,N)  G[i].clear();
        edge_cnt = 0;
        FOR(i,n+1,n+m+1){
            AddEdge(s,i,1);
        }
        FOR(i,1,n+1){
            AddEdge(i,t,1);
        }
        FOR(i,n+1,n+m+1){
            FOR(j,1,n+1){
                if(mat[l[i]][r[j]]){AddEdge(i,j,1);}
            }
        }
    }
    
    int main()
    {
        //freopen("test.in","r",stdin);
        while(~scanf("%d",&n)){
            co = 0;
            FOR(i,1,n+1){
                scanf("%s",src[i]);
                str[co++] = Str(src[i]);
            }
            scanf("%d",&m);
            char tem[30];
            FOR(i,n+1,n+m+1){
                scanf("%s%s",tem,tar[i]);
                str[co++] = Str(tar[i]);
            }
            scanf("%d",&k);
            FOR(i,0,k){
                scanf("%s%s",sr[i],ta[i]);
                str[co++] = Str(sr[i]);
                str[co++] = Str(ta[i]);
            }
            init();
            printf("%d
    ",m-EK());
        }
        return 0;
    }
    POJ_1274 http://poj.org/problem?id=1274 本题是一个裸的二分图最大匹配,正好练一下二分图最大匹配的模板。网络流当然也可以写,一边加源点,一边加汇点,容量都设为1,就变成了一个最大流问题。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <algorithm>
    #include <vector>
    #define ll long long
    #define FOR(i,x,y)  for(int i = x;i < y;i ++)
    #define IFOR(i,x,y) for(int i = x;i > y;i --)
    #define N 250
    
    using namespace std;
    
    vector <int> G[N<<1];
    int n,m;
    int march[N<<1];
    bool vis[N<<1];
    
    bool dfs(int u){
        vis[u] = true;
        FOR(i,0,G[u].size()){
            int v = G[u][i];
            if(!vis[v]){
                vis[v] = true;
                if(march[v] == -1 || dfs(march[v])){
                    march[v] = u;
                    march[u] = v;
                    return true;
                }
            }
        }
        return false;
    }
    
    int MaxMarch(){
        int ans = 0;
        memset(march,-1,sizeof(march));
        FOR(i,1,n+1){
            if(march[i] == -1){
                memset(vis,false,sizeof(vis));
                if(dfs(i))  ans++;
            }
        }
        return ans;
    }
    
    int main()
    {
        //freopen("test.in","r",stdin);
        while(~scanf("%d%d",&n,&m)){
            FOR(i,0,(n+m+1))   G[i].clear();
            int cnt;
            int v;
            FOR(i,1,n+1){
                scanf("%d",&cnt);
                while(cnt--){
                    scanf("%d",&v);
                    G[i].push_back(v+n);
                    G[v+n].push_back(i);
                }
            }
            printf("%d
    ",MaxMarch());
        }
        return 0;
    }

    POJ_1459 http://poj.org/problem?id=1459 题目乍一看有很多源点,汇点,其实都是假的,我们把所有源点全部连到一个s上,对应的路径cap就是这个点的cap,同理,所有的汇点都连到t上,cap对应的就是这个点的cap。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #define FOR(i,x,y)  for(int i = x;i < y;i ++)
    #define IFOR(i,x,y) for(int i = x;i > y;i --)
    #define ll long long
    #define INF (1<<30)
    #define N 220
    
    using namespace std;
    
    inline void readint(int &ret)
    {
        char c;
        do { c = getchar();
        } while(c < '0' || c > '9');
        ret = c - '0';
        while((c=getchar()) >= '0' && c <= '9')
        ret = ret * 10 + ( c - '0' );
    }
    
    int n,np,nc,m;
    
    
    ///wangluoliu xiang guan
    struct Edge{
        int from,to,cap,flow;
        Edge(){}
        Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
    }edge[N*N];
    
    int s,t,edge_cnt,a[N],pre[N];
    bool vis[N];
    vector <int> G[N];
    
    void AddEdge(int u,int v,int c){
        G[u].push_back(edge_cnt);
        edge[edge_cnt++] = Edge(u,v,c,0);
        G[v].push_back(edge_cnt);
        edge[edge_cnt++] = Edge(v,u,0,0);
    }
    
    void init_ntf(){
        FOR(i,0,N)  G[i].clear();
        edge_cnt = 0;
    }
    
    int bfs(){
        memset(vis,false,sizeof(vis));
        queue <int> q;
        q.push(s);
        a[s] = INF;
        vis[s] = true;
        while(!q.empty()){
            int u = q.front(); q.pop();
            if(u == t)  break;
            FOR(i,0,G[u].size()){
                int e = G[u][i];
                int v = edge[e].to;
                if(!vis[v] && edge[e].cap > edge[e].flow){
                    a[v] = min(a[u],edge[e].cap - edge[e].flow);
                    pre[v] = e;
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
        if(!vis[t]) return -1;
        return a[t];
    }
    
    int EK(){
        int max_flow = 0;
        int d;
        while((d = bfs()) != -1){
            max_flow += d;
            for(int u = t; u != s; u = edge[pre[u]].from){
                edge[pre[u]].flow += d;
                edge[pre[u]^1].flow -= d;
            }
        }
        return max_flow;
    }
    
    int main()
    {
        //freopen("test.in","r",stdin);
        while(~scanf("%d%d%d%d",&n,&np,&nc,&m)){
            s = 0;
            t = n+1;
            int u,v,l;
            init_ntf();
            FOR(i,0,m){
                readint(u);
                readint(v);
                readint(l);
                AddEdge(u+1,v+1,l);
            }
            FOR(i,0,np){
                readint(v);
                readint(l);
                AddEdge(s,v+1,l);
            }
            FOR(i,0,nc){
                readint(u);
                readint(l);
                AddEdge(u+1,t,l);
            }
            printf("%d
    ",EK());
        }
        return 0;
    }

    POJ_3281  http://poj.org/problem?id=3281  拆点+最大流。单纯地以为是二分图匹配肯定就贵了(反正我是没想出来二分图要怎么搞)。放到网络流里面,就会发现图建不出来!!!为什么???因为牛这个点可以经过无数遍,这个与题目要求显然不符合,这个时候要怎么办,就是把一头牛拆成牛1,牛2,对应的牛1,牛2之间连接一条容量为1的路径。这个样子就能保证每一头牛都最多走了一次。。。见图的关键就是每一条路径都与每一条路径实际一一对应!!!

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <vector>
    #include <queue>
    #define INF (1<<30)
    #define FOR(i,x,y)  for(int i = x;i < y;i ++)
    #define IFOR(i,x,y) for(int i = x;i > y;i --)
    #define ll long long
    #define N 440
    
    using namespace std;
    
    struct Edge{
        int from,to,cap,flow;
        Edge() {}
        Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
    }edge[N*N*2];
    
    vector <int> G[N];
    int edge_cnt,pre[N],a[N],f,d,n,s,t;
    bool vis[N];
    
    void AddEdge(int u,int v,int c){
        edge[edge_cnt++] = Edge(u,v,c,0);
        edge[edge_cnt++] = Edge(v,u,0,0);
        G[u].push_back(edge_cnt-2);
        G[v].push_back(edge_cnt-1);
    }
    
    int bfs(){
        memset(vis,false,sizeof(vis));
        queue <int> q;
        q.push(s);
        vis[s] = true;
        a[s] = INF;
        while(!q.empty()){
            int u = q.front(); q.pop();
            if(u == t)  break;
            FOR(i,0,G[u].size()){
                int e = G[u][i];
                int v = edge[e].to;
                if(!vis[v] && edge[e].cap > edge[e].flow){
                    a[v] = min(a[u],edge[e].cap - edge[e].flow);
                    pre[v] = e;
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
        if(!vis[t]) return -1;
        return a[t];
    }
    
    int EK(){
        int max_flow = 0;
        int d;
        while((d = bfs()) != -1){
            max_flow += d;
            for(int u = t; u != s; u = edge[pre[u]].from){
                edge[pre[u]].flow += d;
                edge[pre[u]^1].flow -= d;
            }
        }
        return max_flow;
    }
    
    int main()
    {
        //freopen("test.in","r",stdin);
        while(~scanf("%d%d%d",&n,&f,&d)){
            //if(n == 0 || f == 0 || d == 0)  {printf("0
    ");continue;}
            FOR(i,0,N)  G[i].clear();
            edge_cnt = 0;
            s = 0;
            t = f+n+n+d+1;
            FOR(i,1,n+1){
                int cnt_f,cnt_d;
                scanf("%d%d",&cnt_f,&cnt_d);
                FOR(j,0,cnt_f){
                    int u;
                    scanf("%d",&u);
                    AddEdge(u,f+i,1);
                }
                AddEdge(f+i,f+i+n,1);
                FOR(j,0,cnt_d){
                    int v;
                    scanf("%d",&v);
                    AddEdge(f+i+n,f+n+n+v,1);
                }
            }
            FOR(i,1,f+1){
                AddEdge(s,i,1);
            }
            FOR(i,f+n+n+1,t){
                AddEdge(i,t,1);
            }
            printf("%d
    ",EK());
        }
        return 0;
    }


    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    mysql 5.6
    mysql5.7 二进制包安装
    centos 6 编译安装httpd-2.4
    mysql 5.5源码包安装
    BZOJ4945 & 洛谷3825 & UOJ317:[NOI2017]游戏——题解
    BZOJ4943 & 洛谷3823 & UOJ315:[NOI2017]蚯蚓排队——题解
    BZOJ3435 & 洛谷3920 & UOJ55:[WC2014]紫荆花之恋
    BZOJ5343 & 洛谷4602 & LOJ2555:[CTSC2018]混合果汁——题解
    真·APIO2018滚粗记
    BZOJ4518:[SDOI2016]征途——题解
  • 原文地址:https://www.cnblogs.com/hqwhqwhq/p/4811898.html
Copyright © 2011-2022 走看看