[模版]EK算法

本文详细介绍了Edmonds-Karp算法的实现过程,包括从源点开始的最短增广路径搜索、残量网络的更新以及如何找到最大流。通过示例代码展示了如何在C++中应用该算法解决Acwing2171题目,同时指出在实际应用中,EK算法在较大规模问题上也能有效运行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法步骤:

  • 从源点开始,用BFS找一条最短的增广路径,计算该路径上的残量最小值,累加到最大流值;
  • 沿着该路径修改流量值,实际是修改是残量网络的边权;
  • 重复上述步骤,直到找不到增广路时,此时得到的流就是最大流。

时间复杂度: O ( n m 2 ) O(nm^2) O(nm2) n n n是点数, m m m是边数。

但实际情况中,EK算法的复杂度远低于理论上的复杂度,一般 1000 1000 1000~ 10000 10000 10000的规模EK算法都可以解决。

模板题:
Acwing2171.EK求最大流
在这里插入图片描述

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
#define INF 0x3f3f3f3f

using namespace std;

typedef long long LL;

const int N = 1010, M = 20010; //存两个方向的边,M开2倍

int n, m, S, T;
int idx, head[N], e[M], ne[M], c[M]; //c是容量
int q[N], d[N], pre[N]; //q模拟队列, d是从源点到当前路径中所经过的最小容量, pre该点是由那一条边转移过来的
bool vis[N];

void add(int u, int v, int w) //维护的图是残留网络,建立双向边
{
    e[idx] = v, c[idx] = w, ne[idx] = head[u], head[u] = idx ++;
    e[idx] = u, c[idx] = 0, ne[idx] = head[v], head[v] = idx ++;
}

bool bfs()
{
    int hh = 0, tt = -1;
    memset(vis, 0, sizeof vis);
    q[++ tt] = S; //放入起点S
    vis[S] = true, d[S] = INF;

    while(hh <= tt)
    {
        int u = q[hh ++];

        for(int i = head[u]; ~i; i = ne[i])
        {
            int v = e[i];
            if(vis[v] || c[i] == 0) continue; //容量==0是不能走的

            pre[v] = i; //这里记录的是边,不是点!
            d[v] = min(d[u], c[i]);
            
            if(v == T)  return true; //走到了终点

            q[++ tt] = v; vis[v] = true; //将v加入队列
        }
    }

    return false;
}

int EK()
{
    int r = 0;
    while(bfs())
    {
        r += d[T];
        for(int i = T; i != S; i = e[pre[i] ^ 1]) //将S->T的路径经过的边的权值更新
            c[pre[i]] -= d[T], c[pre[i] ^ 1] += d[T];
    }
    return r;
}

int main()
{
#ifdef LOCAL
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif

    memset(head, -1, sizeof head);

    scanf("%d%d%d%d", &n, &m, &S, &T);
    while(m --)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        add(u, v, w);
    }

    printf("%d\n", EK());

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值