ISAP演算法

Visors發表於2020-09-28

ISAP 的意思是 Improved SAP ,即改進的 SAP ,而 SAP(Shortest Augument Path,最短增廣路)演算法其實指的就是 Edmonds-Karp 演算法。但事實上,ISAP 更像是 Dinic ,而不是 SAP。

先貼非遞迴版程式碼,以後再寫原理。

//
// Created by Visors on 2020/9/27.
//
// 題目名:【模板】網路最大流
// 題目來源:luogu
// 題目連結:https://www.luogu.com.cn/problem/P3376
// 演算法:ISAP.cpp
// 用途:TODO
// 時間複雜度:O(TODO)
//

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int oo = 0x7fffffff;

struct Edge {
    int from;       // 起點
    int to;         // 終點
    int capacity;   // 容量
    int flow;       // 流量
    int next;       // 下條邊

    Edge() = default;

    Edge(int from, int to, int capacity, int flow, int next) : from(from), to(to), capacity(capacity), flow(flow), next(next) {}
};

vector<Edge> edges; // 邊集
vector<int> heads;  // 首邊集
vector<int> levels; // 層級
vector<bool> vis;   // 是否增廣
vector<int> cur;    // 當前弧
vector<int> num;    // 每層節點數,用於GAP優化
vector<int> paths;  // 增廣路

int n, m, s, t;

inline void addEdge(int u, int v, int c, int f) {
    edges.emplace_back(v, u, 0, -f, heads[v]);
    heads[v] = edges.size() - 1;
    edges.emplace_back(u, v, c, f, heads[u]);
    heads[u] = edges.size() - 1;
}

bool bfs() {
    fill(vis.begin(), vis.end(), false);
    queue<int> q;
    q.push(t); // 從匯點開始分層
    vis[t] = true;
    levels[t] = 0;
    while (!q.empty()) {
        int v = q.front();
        q.pop();
        for (int i = heads[v]; ~i; i = edges[i].next) {
            // 方向相反,此時反向邊才是入邊
            Edge &e = edges[i ^ 1];
            int &u = e.from;
            if (!vis[u] && e.capacity > e.flow) {
                vis[u] = true;
                levels[u] = levels[v] + 1;
                q.push(u);
            }
        }
    }
    return vis[s];
}

ll augment() {
    int u = t;
    ll imp = oo;
    while (u != s) {
        Edge &e = edges[paths[u]];
        imp = min(imp, (ll)e.capacity - e.flow);
        u = e.from;
    }
    u = t;
    while (u != s) {
        edges[paths[u]].flow += imp;
        edges[paths[u] ^ 1].flow -= imp;
        u = edges[paths[u]].from;
    }
    return imp;
}

ll maxFlow() {
    ll ret = 0;
    bfs(); // ISAP在DFS中動態修改分層levels一遍BFS預處理即可
    fill(num.begin(), num.end(), 0);
    for (int it:levels) num[it]++; // 統計每層節點數
    int u = s;
    for (int i = 0; i < n; i++) cur[i] = heads[i]; // 當前弧初始化
    // 下面其實在模擬DFS
    while (levels[s] < n) {
        // 如果找到匯點,開始增廣
        if (u == t) {
            ret += augment();
            u = s;
        }
        bool ok = false;
        // 找一條可增廣邊
        for (int &i = cur[u]; ~i; i = edges[i].next) {
            Edge &e = edges[i];
            int &v = e.to;
            if (levels[u] == levels[v] + 1 && e.capacity > e.flow) {
                ok = true;
                paths[v] = i;
                u = v;
                break;
            }
        }
        // 如果沒找到,說明正向邊殘量全為0,反向邊殘量>0,更新層數
        if (!ok) {
            int minLevel = n - 1;
            for (int i = heads[u]; ~i; i = edges[i].next) {
                Edge &e = edges[i];
                int &v = e.to;
                // 更新為最小層數+1
                if (e.capacity > e.flow) minLevel = min(minLevel, levels[v]);
            }
            if (--num[levels[u]] == 0) break; // GAP優化
            num[levels[u] = minLevel + 1]++; // 層數變化
            cur[u] = heads[u]; // 當前弧重置
            if (u != s) u = edges[paths[u]].from; // 向前回溯一個節點
        }
    }
    return ret;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    scanf("%d%d%d%d", &n, &m, &s, &t);
    s--, t--;
    edges.clear();
    heads.resize(n, -1);
    levels.resize(n);
    vis.resize(n);
    cur.resize(n);
    num.resize(n);
    paths.resize(n);
    for (int i = 1, u, v, c; i <= m; i++) {
        scanf("%d%d%d", &u, &v, &c);
        addEdge(u - 1, v - 1, c, 0);
    }
    cout << maxFlow() << endl;
    return 0;
}

相關文章