【模板】網路流最大流

phrink發表於2024-08-17

最大流

題目要求:給出n點 m邊 src sink 然後每條邊有 u v capacity 求最大流

題目連結P3376 【模板】網路最大流

EK(Edmonds–Karp)演算法:

\[\begin{align} & \color{Red}時間複雜度O(nm^2) \\ & \color{Red}空間複雜度O(n+m) \\ \end{align} \]

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define int long long 
const int N = 6e5 + 9;  
const int inf2 = 0x7f7f7f7f; 

using namespace std;
#define int long long
#define ios std::ios::sync_with_stdio(false); std::cin.tie(0); 

int n, m, src, sink; 
int ans = 0;          // 最大流量
int head[N];          
int pre[N];           // 儲存前驅邊的陣列 即連線節點u的上一條egde
struct node {
    int to, capacity, next;  
} e[N];
bool vis[N];         
int idx = 1;         
int dist[N];         // 儲存從源節點到各節點的最短路徑流量
int flag[2510][2510]; // 用於標記邊的索引

void add(int u, int v, int val) {
    // 新增正向邊
    e[++idx] = {v, val, head[u]};
    head[u] = idx;
    // 新增反向邊
    e[++idx] = {u, 0, head[v]};
    head[v] = idx;
}

bool bfs() {
    memset(vis, 0, sizeof vis); // 清空訪問標記陣列
    dist[src] = inf2; // 初始化源節點的距離為無窮大
    queue<int> q;
    q.push(src);
    vis[src] = 1;

    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = head[u]; i != 0; i = e[i].next) {
            int v = e[i].to;
            int val = e[i].capacity;
            // 如果邊的容量為 0 或節點 v 已訪問,跳過
            if (e[i].capacity == 0 || vis[v]) continue;
            vis[v] = 1;
            pre[v] = i; // 記錄前驅邊
            dist[v] = min(dist[u], val); // 流量只能取得最小的
            q.push(v); 
            if (v == sink) // 到達sink,返回 true
                return true;
        }
    }
    return false; // 沒有找到增廣路徑
}

void EK() {
    int u = sink;
    while (u != src) {
        int mmin_stream = dist[sink]; // 當前增廣路徑的流量是整條路的最小值
        int last = pre[u]; 
        // 更新正向邊的容量和反向邊的容量
        e[last].capacity -= mmin_stream;
        e[last ^ 1].capacity += mmin_stream;
        u = e[last ^ 1].to; // 更新 u 為反向邊的終點
    }
    ans += dist[sink]; // 增加當前流量到總流量
}

void bd() {
    cin >> n >> m >> src >> sink;
    for (int i = 1; i <= m; ++i) {
        int u, v, val;
        cin >> u >> v >> val; 
        if (flag[u][v] == 0) {
            add(u, v, val); // 新增邊到圖中
            flag[u][v] = idx - 1; // 記錄邊的索引 因為dix起步時=1
        } 
        else 
            e[flag[u][v]].capacity += val; 
            //存在多條邊 容量加起來
    }
}

signed main() {
    ios;
    bd(); 
    while (bfs()) {
        EK();
    }
    cout << ans; // 輸出最大流量a
    return 0;
}

相關文章