Ford-Fulkerson 개요
Ford-Fulkerson 알고리즘은 그래프에서 최대 유량을 계산하는 네트워크 플로우 알고리즘 중 하나입니다. 이 알고리즘은 소스(source)에서 싱크(sink)로의 최대 흐름을 찾기 위해 사용됩니다. 그래프 이론에서 자주 다뤄지는 주제 중 하나로, 다양한 응용 문제에 사용될 수 있습니다. 예를 들어, 파이프 네트워크에서 최대 물의 흐름을 구하거나, 최대 매칭 문제를 해결하는 데 이용됩니다.
Ford-Fulkerson 알고리즘은 증가 경로(augmenting path) 를 찾아 반복적으로 흐름을 증가시키며, 그래프에 유량을 추가하는 방법입니다. 이는 일반적으로 깊이 우선 탐색(DFS)이나 너비 우선 탐색(BFS) 알고리즘을 사용하여 구현됩니다.
이 글에서는 Ford-Fulkerson 알고리즘의 기본 개념과 예제 코드를 Java와 C 언어로 제공하겠습니다.
Ford-Fulkerson 알고리즘의 개념
Ford-Fulkerson 알고리즘은 다음과 같은 과정으로 동작합니다:
- 초기 흐름을 0으로 설정합니다.
- 소스에서 싱크로 가는 경로를 찾습니다. 이 경로는 잔여 용량(residual capacity) 이 있는 경로여야 합니다.
- 잔여 용량 중 최소값만큼 흐름을 추가합니다.
- 경로를 찾을 수 없을 때까지 2-3 단계를 반복합니다.
잔여 용량이란 간선에서 현재 흐름을 제외한 나머지 용량을 의미합니다. Ford-Fulkerson 알고리즘은 유량을 계속해서 증가시키며 최대 유량을 찾습니다.
Ford-Fulkerson의 시간 복잡도는 그래프에서 사용하는 탐색 방법에 따라 달라집니다. 일반적인 경우, BFS를 사용하여 구현하면 O(V * E^2)의 시간 복잡도를 가지며, 이때 V는 정점의 수, E는 간선의 수입니다.
예제 문제 설명
예제 그래프는 다음과 같습니다.
- 정점 6개 (0, 1, 2, 3, 4, 5)
- 간선: (0 -> 1), (0 -> 2), (1 -> 3), (2 -> 4), (3 -> 5), (4 -> 5), (1 -> 4), (3 -> 2)
소스 정점은 0, 싱크 정점은 5입니다. 이 그래프에서 Ford-Fulkerson 알고리즘을 사용하여 최대 유량을 계산해 보겠습니다.
Java 구현
import java.util.*;
public class FordFulkerson {
static final int V = 6; // 정점의 개수
// 깊이 우선 탐색(DFS) 사용
boolean dfs(int[][] residualGraph, int source, int sink, int[] parent) {
boolean[] visited = new boolean[V];
Stack<Integer> stack = new Stack<>();
stack.push(source);
visited[source] = true;
parent[source] = -1;
while (!stack.isEmpty()) {
int u = stack.pop();
for (int v = 0; v < V; v++) {
if (!visited[v] && residualGraph[u][v] > 0) {
stack.push(v);
parent[v] = u;
visited[v] = true;
if (v == sink) return true;
}
}
}
return false;
}
// Ford-Fulkerson 알고리즘
int fordFulkerson(int[][] graph, int source, int sink) {
int[][] residualGraph = new int[V][V];
for (int u = 0; u < V; u++) {
for (int v = 0; v < V; v++) {
residualGraph[u][v] = graph[u][v];
}
}
int[] parent = new int[V];
int maxFlow = 0;
// 증가 경로가 있을 때까지 반복
while (dfs(residualGraph, source, sink, parent)) {
int pathFlow = Integer.MAX_VALUE;
for (int v = sink; v != source; v = parent[v]) {
int u = parent[v];
pathFlow = Math.min(pathFlow, residualGraph[u][v]);
}
// 잔여 용량 업데이트
for (int v = sink; v != source; v = parent[v]) {
int u = parent[v];
residualGraph[u][v] -= pathFlow;
residualGraph[v][u] += pathFlow;
}
maxFlow += pathFlow;
}
return maxFlow;
}
public static void main(String[] args) {
int[][] graph = {
{0, 16, 13, 0, 0, 0},
{0, 0, 10, 12, 0, 0},
{0, 4, 0, 0, 14, 0},
{0, 0, 9, 0, 0, 20},
{0, 0, 0, 7, 0, 4},
{0, 0, 0, 0, 0, 0}
};
FordFulkerson ff = new FordFulkerson();
System.out.println("최대 유량: " + ff.fordFulkerson(graph, 0, 5));
}
}
C 구현
#include <stdio.h>
#include <limits.h>
#include <stdbool.h>
#include <string.h>
#define V 6
bool dfs(int residualGraph[V][V], int source, int sink, int parent[]) {
bool visited[V];
memset(visited, 0, sizeof(visited));
int stack[V], top = -1;
stack[++top] = source;
visited[source] = true;
parent[source] = -1;
while (top >= 0) {
int u = stack[top--];
for (int v = 0; v < V; v++) {
if (!visited[v] && residualGraph[u][v] > 0) {
stack[++top] = v;
parent[v] = u;
visited[v] = true;
if (v == sink) return true;
}
}
}
return false;
}
int fordFulkerson(int graph[V][V], int source, int sink) {
int residualGraph[V][V];
for (int u = 0; u < V; u++) {
for (int v = 0; v < V; v++) {
residualGraph[u][v] = graph[u][v];
}
}
int parent[V];
int maxFlow = 0;
while (dfs(residualGraph, source, sink, parent)) {
int pathFlow = INT_MAX;
for (int v = sink; v != source; v = parent[v]) {
int u = parent[v];
pathFlow = (pathFlow < residualGraph[u][v]) ? pathFlow : residualGraph[u][v];
}
for (int v = sink; v != source; v = parent[v]) {
int u = parent[v];
residualGraph[u][v] -= pathFlow;
residualGraph[v][u] += pathFlow;
}
maxFlow += pathFlow;
}
return maxFlow;
}
int main() {
int graph[V][V] = {
{0, 16, 13, 0, 0, 0},
{0, 0, 10, 12, 0, 0},
{0, 4, 0, 0, 14, 0},
{0, 0, 9, 0, 0, 20},
{0, 0, 0, 7, 0, 4},
{0, 0, 0, 0, 0, 0}
};
printf("최대 유량: %d\n", fordFulkerson(graph, 0, 5));
return 0;
}
마무리
Ford-Fulkerson 알고리즘은 간단한 개념으로 복잡한 네트워크 플로우 문제를 해결할 수 있는 강력한 도구입니다. Java와 C 언어로 각각 구현된 예제를 통해 최대 유량을 찾는 과정을 직접 확인해보세요. 이 알고리즘은 유량을 반복적으로 증가시키며 최적의 흐름을 찾아가는 방식이기 때문에, 다양한 네트워크 문제에서 응용할 수 있습니다.
'알고리즘(Algorithm)' 카테고리의 다른 글
분기 한정 알고리즘 (Branch and Bound) (0) | 2025.02.03 |
---|---|
백트래킹 기법 N-Queen 문제 해결하기 (0) | 2025.02.02 |
문자열 매칭 알고리즘 KMP와 라빈-카프 (0) | 2025.01.31 |
최소 신장 트리 (크루스칼, 프림 알고리즘) (0) | 2025.01.30 |
최단 경로 알고리즘 (다익스트라, 벨만-포드) (0) | 2025.01.29 |