网络流最大流之Dinic算法

题源:https://loj.ac/problem/101

主要参考了这篇博客来学习,个人觉得还是讲的比较通俗易懂的:https://blog.csdn.net/weixin_43907802/article/details/84705855

还是debug了很久。。一开始是抄错模板,后来是忘开了二倍数组和错用了对称性,然后是疯狂爆int。。总而言之,bug真的是一辈子都de不完的。。

贴个代码,注释也直接从模板搬过来了:

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <queue>
#define emax 5005
#define pmax 105
#define INF 10000000000 // (1 << 31) 会爆int(可能是位运算默认只有32位?)
// #define LOCAL
using namespace std;
struct Edge
{
    int next, to;
    long long left;
} edge[emax << 1]; // 记得开双倍。。
int head[pmax], e = -1;

struct Dinic
{
    //cur用于当前弧优化, dep指最小的深度
    int dep[pmax], cur[pmax], s, t, n;
    queue<int> q;
    bool bfs()
    {
        int u, v;
        memset(dep, 0xff, sizeof(dep));
        dep[s] = 0;
        q.push(s);
        while (!q.empty())
        {
            u = q.front(); q.pop();
            for (int i = head[u]; ~i; i = edge[i].next)
            {
                v = edge[i].to;
                if (dep[v] == -1 && edge[i].left > 0) //注意要找增广路,残余容量大于0 (前面是等于-1,一开始抄错了)
                {
                    dep[v] = dep[u] + 1;
                    q.push(v);
                }
            }
        }
        return dep[t] != -1; //d[v]!=-1表示存在s到t的增广路
    }

    //函数的返回类型也要改为long long,这点很容易忽略,一般都是去找变量声明,而忽略了函数声明(其实是自己太菜)
    long long dfs(int u, long long flow) //flow为增广路s->u上最小残余容量,也可以看作是当前可以(或是说能够)流经u的流量吧
    {
        #ifdef LOCAL
        // for (int i = 1; i <= n; ++i) cout << dep[i] << endl;
        #endif
        int v; long long tmp;
        if (u == t || !flow) return flow; //已到汇点,增流flow或者残余容量为0无法增广就返回
        long long f = 0; //f表示经过u的流量有多少
        for (int &i = cur[u]; ~i; i = edge[i].next)
        {
            /*cur当前弧优化,记录访问到的边,由于是从前往后增广,
        访问到某边时,说明前面的边已无法再增广,利用cur跳过前面废边提高效率(注意引用,这点很妙*/
            v = edge[i].to;
            if (dep[v] == dep[u] + 1 && (tmp = dfs(v, min(flow, edge[i].left))))
            {
                #ifdef LOCAL
                cout << u << " -> " << v << " : " << tmp << endl;
                #endif
                edge[i].left -= tmp;
                edge[i ^ 1].left += tmp;
                f += tmp, flow -= tmp;
                //多路增广,尽可能把前面flow的流量都流完再返回
                if (!flow) break;
            }
        }
        return f;
    }
    //注意函数声明返回类型
    long long maxflow(int s, int t, int n)
    {
        this->s = s, this->t = t, this->n = n;
        long long ans = 0;
        while (bfs())
        {
            for (int i = 1; i <= n; ++i) cur[i] = head[i];
            ans += dfs(s, INF); //一开始能流经s的流量是不确定的,因为并没有连向s的路,s为起点,故设为无穷大
        }
        return ans;
    }
};

int main()
{
    #ifdef LOCAL
    freopen("out.txt", "w", stdout);
    #endif
    int n, m, s, t, u, v; long long c; scanf("%d%d%d%d", &n, &m, &s, &t);
    memset(head, 0xff, sizeof(head));
    for (int i = 0; i < m; ++i)
    {
        scanf("%d%d%lld", &u, &v, &c); //lld读入
        edge[++e].next = head[u], edge[e].to = v, edge[e].left = c, head[u] = e;
        edge[++e].next = head[v], edge[e].to = u, edge[e].left = 0, head[v] = e; /* 这里注意不是对称的,反向边left
                                                                                    应设为0,以后也要注意类似的对称模式 */

    }
    Dinic solve;
    cout << solve.maxflow(s, t, n);
}

还有一点没想通就是如果数据中同时存在两个点的正反向边,那么后加入的边的反向边可能会覆盖在前一条边上,会不会出现什么问题。。不过数据还是过了,很玄学

猜你喜欢

转载自www.cnblogs.com/jionkitten/p/12215115.html