에드몬드-카프(Edmonds-Karp) 알고리즘이란?

에드몬드-카프 알고리즘은 포드-풀커슨(Ford-Fulkerson) 방법을 기반으로 네트워크에서 최대 유량을 찾는 알고리즘입니다.

DFS를 이용해 증가 경로(Augmenting Path)를 찾는 구체적인 방법을 명시하지 않는 것과 달리, 에드몬드-카프 알고리즘은 너비 우선 탐색(BFS) 을 사용하여 가장 짧은 증가 경로를 찾는다는 점이 핵심적인 차이입니다.


특징

  • 증가 경로 탐색: 소스(S)에서 싱크(T)까지의 증가 경로를 너비 우선 탐색(BFS)으로 찾습니다.
  • 최단 경로 보장: BFS를 사용하므로, 간선의 수가 가장 적은 증가 경로를 항상 먼저 찾게 됩니다.
  • 시간 복잡도: 이 방식은 포드-풀커슨 방법의 시간 복잡도를 개선하여, 특정 상황에서 더 효율적으로 동작합니다.

코드

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
 
using namespace std;
 
const int INF = 1e9;
const int MAX_V = 50; // 최대 정점 개수
 
int V; // 정점의 수
int capacity[MAX_V][MAX_V]; // capacity[u][v]: u -> v 간선의 용량
vector<int> adj[MAX_V];     // 인접 리스트
 
// 에드몬드-카프 알고리즘
int max_flow(int source, int sink) {
    int total_flow = 0;
 
    // 증가 경로가 없을 때까지 반복
    while (true) {
        // BFS를 사용하여 증가 경로를 찾음
        vector<int> parent(V, -1);
        queue<int> q;
 
        parent[source] = source;
        q.push(source);
 
        while (!q.empty() && parent[sink] == -1) {
            int u = q.front();
            q.pop();
 
            for (int v : adj[u]) {
                // 방문하지 않았고, 잔여 용량이 남아있는 경우
                if (parent[v] == -1 && capacity[u][v] > 0) {
                    parent[v] = u;
                    q.push(v);
                }
            }
        }
 
        // 싱크에 도달하는 증가 경로가 없으면 종료
        if (parent[sink] == -1) {
            break;
        }
 
        // 경로의 최소 잔여 용량(보낼 수 있는 유량)을 찾음
        int path_flow = INF;
        for (int v = sink; v != source; v = parent[v]) {
            int u = parent[v];
            path_flow = min(path_flow, capacity[u][v]);
        }
 
        // 경로에 유량을 흘려보냄 (잔여 용량 업데이트)
        for (int v = sink; v != source; v = parent[v]) {
            int u = parent[v];
            capacity[u][v] -= path_flow; // 정방향 간선 용량 감소
            capacity[v][u] += path_flow; // 역방향(유령) 간선 용량 증가
        }
 
        // 총 유량에 더함
        total_flow += path_flow;
    }
 
    return total_flow;
}
 
int main() {
    // 예제 그래프 설정
    // 6개의 정점 (0 ~ 5)
    // 소스: 0, 싱크: 5
    V = 6;
    int source = 0, sink = 5;
 
    // 간선과 용량 추가 (u, v, cap)
    vector<tuple<int, int, int>> edges = {
        {0, 1, 16}, {0, 2, 13}, {1, 2, 10}, {1, 3, 12}, {2, 1, 4},
        {2, 4, 14}, {3, 2, 9},  {3, 5, 20}, {4, 3, 7},  {4, 5, 4}
    };
    
    for (const auto& edge : edges) {
        int u, v, cap;
        tie(u, v, cap) = edge;
        adj[u].push_back(v);
        adj[v].push_back(u); // 역방향 경로 탐색을 위해 추가
        capacity[u][v] = cap;
    }
 
    cout << "최대 유량: " << max_flow(source, sink) << endl; // 결과: 23
 
    return 0;
}

자세한 유량 계산 과정은 최대 유량(Maximum Flow) 문제 의 포드-풀커슨 방법과 동일합니다.