【模板】网络流最大流

最大流

题目要求:给出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;
}

Dinic算法

思路

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <climits>

using namespace std;
#define int long long 
const int  inf = 2005020600;
const int N = 2e6 + 9;

int m, src, sink, n;
int  ans, dist[N];
int idx = 1, now[N], head[N];

struct node {
    int to, next;
    int  capacity;
} e[N];

void add(int u, int v, int  val) {
    // 添加正向边
    e[++idx] = {v, head[u], val}; // 添加从u到v的边,容量为val
    head[u] = idx; // 更新u的链表头指针
    
    // 添加反向边
    e[++idx] = {u, head[v], 0}; // 添加从v到u的反向边,容量为0
    head[v] = idx; // 更新v的链表头指针
}

int bfs() {  // 在残量网络中构造分层图
    fill(dist, dist + n + 1, inf); // 初始化所有节点的距离为无穷大
    
    queue<int> q;
    q.push(src); // 从源点开始
    dist[src] = 0; // 源点的距离为0
    now[src] = head[src]; // 初始化当前弧
    
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        for (int i = head[x]; i!=0; i = e[i].next) {
            int v = e[i].to;
            if (e[i].capacity > 0 && dist[v] == inf) {
                q.push(v); // 将v加入队列
                now[v] = head[v]; // 更新v的当前弧
                dist[v] = dist[x] + 1; // 更新v的距离
                if (v == sink) 
                    return true; // 如果找到汇点,返回1
            }
        }
    }
    return false; // 如果没有找到汇点,返回0
}

int  dfs(int x, int  sum) {  // sum是整条增广路对最大流的贡献
    if (x == sink) return sum; // 如果到达汇点,返回流量
    
    int  k, res = 0; // k是当前最小的剩余容量
    for (int i = now[x]; i && sum; i = e[i].next) {
        now[x] = i; // 当前弧优化
        int v = e[i].to;
        if (e[i].capacity > 0 && (dist[v] == dist[x] + 1)) {
            k = dfs(v, min(sum, e[i].capacity)); // 递归寻找增广路径
            if (k == 0) dist[v] = inf; // 剪枝,去掉增广完毕的点
            e[i].capacity -= k; // 更新正向边的容量
            e[i ^ 1].capacity += k; // 更新反向边的容量
            res += k; // 计算流量
            sum -= k; // 更新剩余流量
        }
    }
    return res; // 返回经过当前点的所有流量和
}
int dinic(){
    while (bfs()) {
        ans += dfs(src, inf); 
    }
    return ans;
}
signed main() {
    cin >> n >> m >> src >> sink; 
    for (int i = 1; i <= m; i++) {
        int u, v;
        int  val;
        cin >> u >> v >> val; 
        add(u, v, val); 
    }
    cout <<dinic(); // 输出最大流
    return 0;
}

posted @ 2024-08-17 18:12  phrink  阅读(31)  评论(0)    收藏  举报