서울에서 부산까지 고속도로 네트워크를 통해 한 시간에 최대 몇 대의 차량을 보낼 수 있을까요? 이것이 네트워크 플로우 문제입니다.

네트워크 플로우란

네트워크 플로우(Network Flow)는 소스(Source)에서 싱크(Sink)까지 보낼 수 있는 최대 유량 을 구하는 문제입니다.

기본 용어

  • 소스(S): 유량이 시작되는 노드
  • ** 싱크(T)**: 유량이 도착하는 노드
  • ** 용량(Capacity)**: 각 간선이 처리할 수 있는 최대 유량
  • ** 유량(Flow)**: 실제로 흐르는 양
  • ** 잔여 용량(Residual Capacity)**: 용량 - 유량

유량의 조건

  1. ** 용량 제한 **: 각 간선의 유량 ≤ 용량
  2. ** 유량 보존 **: 소스와 싱크를 제외한 모든 노드에서 들어오는 유량 = 나가는 유량
  3. ** 반대칭 **: f(u,v) = -f(v,u)

왜 필요한가

  • 네트워크 대역폭 계산
  • 이분 매칭 (작업 배정)
  • 최소 컷 (네트워크 취약점)
  • 서버 용량 설계

Ford-Fulkerson 방법

핵심 아이디어

  1. 소스에서 싱크까지의 ** 증가 경로(Augmenting Path)**를 찾는다
  2. 그 경로를 따라 가능한 최대 유량을 보낸다
  3. ** 잔여 그래프(Residual Graph)**를 갱신한다
  4. 더 이상 증가 경로가 없을 때까지 반복한다

잔여 그래프

유량을 보낸 후 남은 용량을 나타내는 그래프입니다. 핵심은 ** 역방향 간선 **입니다.

PLAINTEXT
원래: A →(10)→ B  (용량 10)
유량 6을 보낸 후:
  순방향: A →(4)→ B   (잔여 용량 = 10-6 = 4)
  역방향: B →(6)→ A   (되돌릴 수 있는 유량 = 6)

역방향 간선은 "이미 보낸 유량을 취소"하는 것을 허용합니다. 이를 통해 유량을 재분배하여 전체 최대 유량을 달성합니다.

왜 역방향 간선이 필요한가

PLAINTEXT
    S →(1)→ A →(1)→ T
    S →(1)→ B →(1)→ T
    A →(1)→ B

경로 1: S→A→B→T (유량 1)
이제 S→A는 꽉 참. 하지만 S→B→... 도 가야 함.
역방향 간선 B→A를 통해:
경로 2: S→B→A→T (유량 1, B→A는 역방향으로 A→B의 유량 취소)

결과: 총 유량 2 (S→A→T: 1, S→B→T: 1)

Edmonds-Karp 알고리즘

Ford-Fulkerson에서 증가 경로를 BFS 로 찾는 구체적인 알고리즘입니다.

JAVA
public class EdmondsKarp {
    static int[][] capacity;
    static int[][] flow;
    static int n;

    public static int maxFlow(int source, int sink) {
        int totalFlow = 0;

        while (true) {
            // BFS로 증가 경로 찾기
            int[] parent = bfs(source, sink);
            if (parent == null) break; // 증가 경로 없음

            // 경로를 따라 보낼 수 있는 최대 유량 계산
            int pathFlow = Integer.MAX_VALUE;
            for (int v = sink; v != source; v = parent[v]) {
                int u = parent[v];
                pathFlow = Math.min(pathFlow,
                    capacity[u][v] - flow[u][v]);
            }

            // 유량 갱신
            for (int v = sink; v != source; v = parent[v]) {
                int u = parent[v];
                flow[u][v] += pathFlow;
                flow[v][u] -= pathFlow; // 역방향
            }

            totalFlow += pathFlow;
        }
        return totalFlow;
    }

    private static int[] bfs(int source, int sink) {
        int[] parent = new int[n];
        Arrays.fill(parent, -1);
        parent[source] = source;

        Queue<Integer> queue = new LinkedList<>();
        queue.offer(source);

        while (!queue.isEmpty()) {
            int u = queue.poll();

            for (int v = 0; v < n; v++) {
                // 잔여 용량이 있고 아직 방문하지 않은 노드
                if (parent[v] == -1 && capacity[u][v] - flow[u][v] > 0) {
                    parent[v] = u;
                    if (v == sink) return parent;
                    queue.offer(v);
                }
            }
        }
        return null; // 싱크에 도달 불가
    }
}

시간 복잡도

  • Ford-Fulkerson (DFS): O(E × maxFlow) — 유량이 큰 경우 비효율적
  • Edmonds-Karp (BFS): O(VE²) — 항상 다항 시간 보장

최대 유량-최소 컷 정리

** 최대 유량 = 최소 컷의 용량**

** 컷(Cut)**: 소스와 싱크를 분리하는 간선 집합 ** 최소 컷 **: 용량의 합이 최소인 컷

PLAINTEXT
    S →(3)→ A →(2)→ T
    S →(2)→ B →(3)→ T

최대 유량 = 5 (S→A→T: 2, S→B→T: 2, S→A→... 추가 1)
아니, 재계산:
  S→A: 3, A→T: 2 → 병목 2
  S→B: 2, B→T: 3 → 병목 2
  최대 유량 = 4?

정확한 계산은 간선 구조에 따라 다르지만,
핵심: 최소 컷 = 네트워크에서 가장 취약한 지점의 총 용량

최소 컷 구하기

최대 유량 알고리즘 실행 후, 잔여 그래프에서 소스에서 도달 가능한 노드 집합 S와 나머지 T를 나누면 최소 컷입니다.

JAVA
// 최소 컷: 잔여 그래프에서 소스에서 BFS 도달 가능한 노드 집합
public static Set<Integer> minCut(int source) {
    Set<Integer> reachable = new HashSet<>();
    Queue<Integer> queue = new LinkedList<>();
    queue.offer(source);
    reachable.add(source);

    while (!queue.isEmpty()) {
        int u = queue.poll();
        for (int v = 0; v < n; v++) {
            if (!reachable.contains(v) && capacity[u][v] - flow[u][v] > 0) {
                reachable.add(v);
                queue.offer(v);
            }
        }
    }
    return reachable;
    // 최소 컷 간선: reachable에서 !reachable로 가는 포화된 간선
}

이분 매칭

이분 그래프에서 최대 매칭을 네트워크 플로우로 해결할 수 있습니다.

PLAINTEXT
왼쪽 그룹: 사람 {A, B, C}
오른쪽 그룹: 업무 {1, 2, 3}
간선: 수행 가능한 조합

네트워크 구성:
  S → A(1), S → B(1), S → C(1)     // 소스에서 왼쪽으로 (용량 1)
  A → 1(1), A → 2(1)               // 원래 간선 (용량 1)
  B → 2(1), B → 3(1)
  C → 1(1), C → 3(1)
  1 → T(1), 2 → T(1), 3 → T(1)    // 오른쪽에서 싱크로 (용량 1)

최대 유량 = 최대 매칭

Hungarian 알고리즘

이분 매칭에 특화된 알고리즘으로, Hopcroft-Karp 알고리즘은 O(E√V)에 최대 매칭을 구합니다.

JAVA
// 간단한 이분 매칭 (DFS 기반)
static int[] match; // match[v] = v와 매칭된 왼쪽 노드 (-1이면 미매칭)

public static int maxMatching(List<List<Integer>> adj, int leftSize, int rightSize) {
    match = new int[rightSize];
    Arrays.fill(match, -1);
    int result = 0;

    for (int u = 0; u < leftSize; u++) {
        boolean[] visited = new boolean[rightSize];
        if (dfsMatch(adj, u, visited)) {
            result++;
        }
    }
    return result;
}

private static boolean dfsMatch(List<List<Integer>> adj, int u, boolean[] visited) {
    for (int v : adj.get(u)) {
        if (!visited[v]) {
            visited[v] = true;
            // v가 미매칭이거나, v의 현재 매칭 상대를 다른 곳에 배정할 수 있으면
            if (match[v] == -1 || dfsMatch(adj, match[v], visited)) {
                match[v] = u;
                return true;
            }
        }
    }
    return false;
}

Dinic 알고리즘

Edmonds-Karp보다 빠른 O(V²E) 알고리즘입니다. BFS로 레벨 그래프를 구성하고, DFS로 차단 유량(blocking flow)을 찾습니다.

JAVA
// Dinic의 핵심 구조
// 1. BFS로 레벨 그래프 구성 (각 노드의 거리 레벨)
// 2. DFS로 차단 유량 찾기 (레벨이 증가하는 방향으로만 탐색)
// 3. 1-2를 반복하여 더 이상 증가 경로가 없을 때까지

단위 용량 그래프(모든 간선 용량 1)에서는 O(E√V)로 더 빠릅니다. 이분 매칭에 적합합니다.

백엔드/실무에서의 연결

서버 용량 설계

서비스 아키텍처를 네트워크 플로우로 모델링하면 시스템의 ** 최대 처리량(throughput)**을 계산할 수 있습니다.

PLAINTEXT
클라이언트 → 로드밸런서(1000rps) → 서버A(300rps)
                                 → 서버B(500rps)
                                 → 서버C(400rps) → DB(800rps)

최대 처리량 = min(1000, 300+500+400, 800) = 800rps
→ DB가 병목 (최소 컷)

네트워크 대역폭

인터넷 백본 네트워크에서 두 지점 간 최대 전송 속도를 계산하는 것은 최대 유량 문제입니다.

CDN 트래픽 라우팅

CDN에서 사용자에게 콘텐츠를 전달하는 최적 경로를 결정할 때, 각 링크의 대역폭을 용량으로 모델링합니다.

프로젝트 할당

개발자를 프로젝트에 배정하는 문제는 이분 매칭입니다. 각 개발자가 수행 가능한 프로젝트 목록이 주어질 때, 최대한 많은 프로젝트를 수행할 수 있도록 배정합니다.

주의할 점

역방향 간선을 추가하지 않는 실수

Ford-Fulkerson에서 증가 경로를 통해 유량을 보낼 때, ** 역방향 간선에 유량을 더해야** 합니다. 역방향 간선이 없으면 유량을 되돌릴 수 없어 최대 유량을 찾지 못합니다.

BFS 대신 DFS를 사용하여 시간 초과 (Edmonds-Karp)

DFS 기반 Ford-Fulkerson은 비합리적인 경로를 반복 선택하여 수렴이 느릴 수 있습니다. BFS를 사용하는 Edmonds-Karp는 O(VE^2)을 보장합니다.


정리

알고리즘시간 복잡도특징
Ford-FulkersonO(E × maxFlow)유량이 작을 때 적합
Edmonds-KarpO(VE²)BFS 기반, 항상 다항 시간
DinicO(V²E)레벨 그래프 + 차단 유량
Hopcroft-KarpO(E√V)이분 매칭 특화

기억할 포인트:

  • 네트워크 플로우의 핵심은 ** 잔여 그래프와 역방향 간선 **입니다.
  • ** 최대 유량 = 최소 컷 **은 네트워크의 병목을 찾는 강력한 도구입니다.
  • 이분 매칭, 작업 배정, 서버 용량 설계 등 다양한 문제가 네트워크 플로우로 모델링됩니다.
  • 실무에서 시스템의 처리량 병목을 분석할 때, 최소 컷 개념이 직접적으로 활용됩니다.
댓글 로딩 중...