zoukankan      html  css  js  c++  java
  • 网络流 之 dinic算法

    我觉得这个dinic的算法和之前的增广路法差不多
    1、使用BFS对残余网络进行分层,在分层时,只要进行到汇点的层次数被算出即可停止,
    因为按照该DFS的规则,和汇点同层或更下一层的节点,是不可能走到汇点的。
    
    2、分完层后,从源点开始,用DFS从前一层向后一层反复寻找增广路(即要求DFS的每一步都必须要走到下一层的节点)。
    
    3、DFS过程中,要是碰到了汇点,则说明找到了一条增广路径。此时要增加总流量的值,消减路径上各边的容量,
    并添加反向边,即所谓的进行增广。
    
    4、DFS找到一条增广路径后,并不立即结束,而是回溯后继续DFS寻找下一个增广路径。回溯到的结点满足以下的条件:
    
      1) DFS搜索树的树边(u,v)上的容量已经变成0。即刚刚找到的增广路径上所增加的流量,等于(u,v)本次增广前的容量。
        (DFS的过程中,是从u走到更下层的v的)
      2) u是满足条件 1)的最上层的节点。
    
    5、如果回溯到源点而且无法继续往下走了,DFS结束。因此,一次DFS过程中,可以找到多条增广路径。
    6、DFS结束后,对残余网络再次进行分层,然后再进行DFS当残余网络的分层操作无法算出汇点的层次(即BFS到达不了汇点)时,
    算法结束,最大流求出。
    
    
    
    
    #include <cstdio>
    #include <cstdlib>
    #include <vector>
    #include <queue>
    #define INF 0x3f3f3f3f
    using namespace std;
    const int maxn = 1e5;
    struct edge
    {
        int u, v, c, f;
        edge(int u, int v, int c, int f) :u(u), v(v), c(c), f(f) {}
    };
    vector<edge>e;
    vector<int>G[maxn];
    int level[maxn];//BFS分层,表示每个点的层数
    int iter[maxn];//当前弧优化
    int m;
    void init(int n)
    {
        for (int i = 0; i <= n; i++)G[i].clear();
        e.clear();
    }
    void addedge(int u, int v, int c)
    {
        e.push_back(edge(u, v, c, 0));
        e.push_back(edge(v, u, 0, 0));
        m = e.size();
        G[u].push_back(m - 2);
        G[v].push_back(m - 1);
    }
    void BFS(int s)//预处理出level数组
    //直接BFS到每个点
    {
        memset(level, -1, sizeof(level));
        queue<int>q;
        level[s] = 0;
        q.push(s);
        while (!q.empty())
        {
            int u = q.front();
            q.pop();
            for (int v = 0; v < G[u].size(); v++)
            {
                edge& now = e[G[u][v]];
                if (now.c > now.f && level[now.v] < 0)
                {
                    level[now.v] = level[u] + 1;
                    q.push(now.v);
                }
            }
        }
    }
    int dfs(int u, int t, int f)//DFS寻找增广路
    {
        if (u == t)return f;//已经到达源点,返回流量f
        for (int &v = iter[u]; v < G[u].size(); v++)
            //这里用iter数组表示每个点目前的弧,这是为了防止在一次寻找增广路的时候,对一些边多次遍历
            //在每次找增广路的时候,数组要清空
        {
            edge &now = e[G[u][v]];
            if (now.c - now.f > 0 && level[u] < level[now.v])
                //now.c - now.f > 0表示这条路还未满
                //level[u] < level[now.v]表示这条路是最短路,一定到达下一层,这就是Dinic算法的思想
            {
                int d = dfs(now.v, t, min(f, now.c - now.f));
                if (d > 0)
                {
                    now.f += d;//正向边流量加d
                    e[G[u][v] ^ 1].f -= d;
                    //反向边减d,此处在存储边的时候两条反向边可以通过^操作直接找到
                    return d;
                }
            }
        }
        return 0;
    }
    int Maxflow(int s, int t)
    {
        int flow = 0;
        for (;;)
        {
            BFS(s);
            if (level[t] < 0)return flow;//残余网络中到达不了t,增广路不存在
            memset(iter, 0, sizeof(iter));//清空当前弧数组
            int f;//记录增广路的可增加的流量
            while ((f = dfs(s, t, INF)) > 0)
            {
                flow += f;
            }
        }
        return flow;
    }

    接下来贴一个模板题,但是这个题目的图有点难建

    C - Dining

     POJ - 3281 

    Cows are such finicky eaters. Each cow has a preference for certain foods and drinks, and she will consume no others.

    Farmer John has cooked fabulous meals for his cows, but he forgot to check his menu against their preferences. Although he might not be able to stuff everybody, he wants to give a complete meal of both food and drink to as many cows as possible.

    Farmer John has cooked F (1 ≤ F ≤ 100) types of foods and prepared D (1 ≤ D ≤ 100) types of drinks. Each of his N (1 ≤ N ≤ 100) cows has decided whether she is willing to eat a particular food or drink a particular drink. Farmer John must assign a food type and a drink type to each cow to maximize the number of cows who get both.

    Each dish or drink can only be consumed by one cow (i.e., once food type 2 is assigned to a cow, no other cow can be assigned food type 2).

    Input

    Line 1: Three space-separated integers: NF, and D 
    Lines 2.. N+1: Each line i starts with a two integers Fi and Di, the number of dishes that cow i likes and the number of drinks that cow i likes. The next Fiintegers denote the dishes that cow i will eat, and the Di integers following that denote the drinks that cow i will drink.

    Output

    Line 1: A single integer that is the maximum number of cows that can be fed both food and drink that conform to their wishes

    Sample Input

    4 3 3
    2 2 1 2 3 1
    2 2 2 3 1 2
    2 2 1 3 1 2
    2 1 1 3 3

    Sample Output

    3

    Hint

    One way to satisfy three cows is: 
    Cow 1: no meal 
    Cow 2: Food #2, Drink #2 
    Cow 3: Food #1, Drink #1 
    Cow 4: Food #3, Drink #3 
    The pigeon-hole principle tells us we can do no better since there are only three kinds of food or drink. Other test data sets are more challenging, of course.
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    #include <queue>
    #include <cstring>
    #include <vector>
    #define inf 0x3f3f3f3f
    using namespace std;
    const int maxn = 1e5 + 10;
    int n, f, d;
    struct node
    {
        int from, to, cap, flow;
        node(int from = 0, int to = 0, int cap = 0, int flow = 0) :from(from), to(to), cap(cap), flow(flow) {}
    };
    vector<node>e;
    vector<int>G[maxn];
    int level[maxn], iter[maxn];
    void add(int u, int v, int w)
    {
        e.push_back(node(u, v, w, 0));
        e.push_back(node(v, u, 0, 0));
        int m = e.size();
        G[u].push_back(m - 2);
        G[v].push_back(m - 1);
    }
    
    void bfs(int s)//这个是为了构建层次网络,也就是level的构建
    {
        memset(level, -1, sizeof(level));
        queue<int>que;
        que.push(s);
        level[s] = 0;
        while (!que.empty())
        {
            int u = que.front(); que.pop();
            for (int i = 0; i < G[u].size(); i++)
            {
                node &now = e[G[u][i]];
                if (now.cap > now.flow&&level[now.to] < 0)//只有这个没有满并且没有被访问过才可以被访问
                {
                    level[now.to] = level[u] + 1;
                    que.push(now.to);
                }
            }
        }
    }
    
    int dfs(int u, int v, int f)
    {
        if (u == v) return f;
        for (int &i = iter[u]; i < G[u].size(); i++)
        {
            node &now = e[G[u][i]];
            if (now.cap > now.flow&&level[now.to] > level[u])
            {
                int d = dfs(now.to, v, min(f, now.cap - now.flow));
                if (d > 0)
                {
                    now.flow += d;
                    e[G[u][i] ^ 1].flow -= d;
                    return d;
                }
            }
        }
        return 0;
    }
    
    int Maxflow(int s, int t)
    {
        int flow = 0;
        while (1)
        {
            bfs(s);
            if (level[t] < 0) return flow;
            memset(iter, 0, sizeof(iter));
            int f;
            while ((f = dfs(s, t, inf) > 0)) flow += f;
        }
    }
    void init()
    {
        for (int i = 0; i <= n + 5; i++) G[i].clear();
        e.clear();
    }
    
    int main()
    {
        while (cin >> n >> f >> d)
        {
            init();
            int s = 0, t = f + 2 * n + d + 1;
            for (int i = 1; i <= f; i++) add(s, i, 1);
            for (int i = 1; i <= n; i++)
            {
                int a, b;
                cin >> a >> b;
                while (a--)//与牛i相连
                {
                    int x;
                    cin >> x;
                    add(x, f + i, 1);
                }
                add(f + i, f + n + i, 1);
                while (b--)
                {
                    int x;
                    cin >> x;
                    add(f + n + i, f + 2 * n + x, 1);
                }
            }
            for (int i = 1; i <= d; i++) add(f + 2 * n + i, t, 1);
            int ans = Maxflow(s, t);
            cout << ans << endl;
        }
        return 0;
    }
  • 相关阅读:
    生成器函数
    初识函数
    格式化输出
    流程控制-while循环
    流程控制if语句
    Python入门
    Eclipse搭建C++开发环境
    Android SQLite基本用法
    Android拍照后更新相册
    操作系统相册和获取相册路径
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/10732145.html
Copyright © 2011-2022 走看看