zoukankan      html  css  js  c++  java
  • 网络流EdmondsKarp算法模板理解

    先推荐一个讲网络流的博客,我的网络流知识均吸收于此   传送门

    EdmondsKarp算法基本思想:从起点到终点进行bfs,只要存在路,说明存在增广路径,则取这部分路 权值最小的一部分,即为增广路径(也就是这一部分路的最大流量)。然后将这条路上的正向权值都减去min,反向权值都加上min(即,m[i][j]-min,m[j][i]+min,为什么等会再解释)。然后重复此操作,最终就得到了最大流。

    先上模板(也是取自于刚才的博客,真的写的很精简很好懂)。

    邻接矩阵版本。

    #include <queue>  
    #include <cstdio>  
    #include <cstring>  
    #include <iostream>  
    using namespace std;  
    const int MAXN = 300;  
    const int MAX_INT = ((1 << 31) - 1);  
      
    int n;                                      // 图中点的数目  
    int pre[MAXN];                              // 从 s - t 中的一个可行流中, 节点 i 的前序节点为 Pre[i];  
    bool vis[MAXN];                             // 标记一个点是否被访问过  
    int mp[MAXN][MAXN];                         // 记录图信息  
      
    bool bfs(int s, int t){  
        queue <int> que;  
        memset(vis, 0, sizeof(vis));  
        memset(pre, -1, sizeof(pre));  
        pre[s] = s;  
        vis[s] = true;  
        que.push(s);  
        while(!que.empty()){  
            int u = que.front();  
            que.pop();  
            for(int i = 1; i <= n; i++){  
                if(mp[u][i] && !vis[i]){  
                    pre[i] = u;  
                    vis[i] = true;  
                    if(i == t) return true;  
                    que.push(i);  
                }  
            }  
        }  
        return false;  
    }  
      
    int EK(int s, int t){  
        int ans = 0;  
        while(bfs(s, t)){  
            int mi = MAX_INT;  
            for(int i = t; i != s; i = pre[i]){  
                mi = min(mi, mp[pre[i]][i]);  
            }  
            for(int i = t; i != s; i = pre[i]){  
                mp[pre[i]][i] -= mi;  
                mp[i][pre[i]] += mi;  
            }  
            ans += mi;  
        }  
        return ans;  
    }  
     

    这部分代码唯一不好理解的就是,思考了很久,终于在跑步时想通了这部分代码的原理。

    for(int i = t; i != s; i = pre[i]){  
                mp[pre[i]][i] -= mi;  
                mp[i][pre[i]] += mi;  
            } 

    mp[pre[i]][i]-mi好理解,因为已经流过了嘛,那为什么有mp[i][pre[i]]+=mi呢?让我们看一个具体的例子。

                                              

    按照我们的做法,第一次得到了1-2-4-6这条路径,会发现最大流此时是2,然后将路径上的值全部减去2,然后得到的图是这样的:

                                              

    如果只减掉了正向的路径的权值,这个图就不连通了,也就是答案是2,然而其实答案是3,为什么呢,让我们加上反向的权值看一下:

                                              

    红色的就是反向的权值,然后再进行上述过程,会发现还有一条 1-3-4-2-5-6的路径,权值为1,所以答案是2+1=3.(这个答案大家可以根据第一幅图自己想一下,会发现就是3,好神奇有木有?)

          那为什么这么神奇呢,我的理解是,反向路径代表了,这条路曾经通了多少水,比如2-4这条就通2滴水,然后在第二条路径中通过的这1滴水,其实就是2号路径跟1号路径说,你原本在2-4这条路上有两滴水,现在分一滴到我想去的地方,然后我的水去你想去的地方,这样我们都能实现目标。(想一下是不是这么回事)。

          解决了这个难题,那EK算法的模板你就理解啦!

          接下来送上邻接表的版本。

    const int MAXN = 430;  
    const int MAX_INT = (1 << 30);  
      
    struct Edge{  
        int v, nxt, w;  
    };  
      
    struct Node{  
        int v, id;  
    };  
      
    int n, m, ecnt;  
    bool vis[MAXN];  
    int head[MAXN];  
    Node pre[MAXN];  
    Edge edge[MAXN];  
      
    void init(){  
        ecnt = 0;  
        memset(edge, 0, sizeof(edge));  
        memset(head, -1, sizeof(head));  
    }  
      
    void addEdge(int u, int v, int w){  
        edge[ecnt].v = v;  
        edge[ecnt].w = w;  
        edge[ecnt].nxt = head[u];  
        head[u] = ecnt++;  
    }  
      
    bool bfs(int s, int t){  
        queue <int> que;  
        memset(vis, 0, sizeof(vis));  
        memset(pre, -1, sizeof(pre));  
        pre[s].v = s;  
        vis[s] = true;  
        que.push(s);  
        while(!que.empty()){  
            int u = que.front();  
            que.pop();  
            for(int i = head[u]; i + 1; i = edge[i].nxt){  
                int v = edge[i].v;  
                if(!vis[v] && edge[i].w){  
                    pre[v].v = u;  
                    pre[v].id = i;  
                    vis[v] = true;  
                    if(v == t) return true;  
                    que.push(v);  
                }  
            }  
        }  
        return false;  
    }  
      
    int EK(int s, int t){  
        int ans = 0;  
        while(bfs(s, t)){  
            int mi = MAX_INT;  
            for(int i = t; i != s; i = pre[i].v){  
                mi = min(mi, edge[pre[i].id].w);  
            }  
            for(int i = t; i != s; i = pre[i].v){  
                edge[pre[i].id].w -= mi;  
                edge[pre[i].id ^ 1].w += mi;  
            }  
            ans += mi;  
        }  
        return ans;  
    }  
      
    // 加边  
    addEdge(u, v, w);  
    addEdge(v, u, 0);  
    // 调用  
    int ans = EK(s, t);  


  • 相关阅读:
    June. 26th 2018, Week 26th. Tuesday
    June. 25th 2018, Week 26th. Monday
    June. 24th 2018, Week 26th. Sunday
    June. 23rd 2018, Week 25th. Saturday
    June. 22 2018, Week 25th. Friday
    June. 21 2018, Week 25th. Thursday
    June. 20 2018, Week 25th. Wednesday
    【2018.10.11 C与C++基础】C Preprocessor的功能及缺陷(草稿)
    June.19 2018, Week 25th Tuesday
    June 18. 2018, Week 25th. Monday
  • 原文地址:https://www.cnblogs.com/mountaink/p/9536729.html
Copyright © 2011-2022 走看看