이분 매칭 (Bipartite Matching)
정의
이분 매칭 (Bipartite Matching) 은 이분 그래프 G = (L ∪ R, E) 에서 간선 부분집합 M ⊆ E 을 선택하되, M 의 어떤 두 간선도 정점을 공유하지 않는 (disjoint endpoints) 최대 크기 M.
수학적: M 이 매칭 ↔ 모든 v ∈ V 에 대해 M 의 간선이 v 를 많아야 한 번 포함.
최대 매칭 (maximum matching): 가능한 M 중 |M| 이 최대.
알고리즘:
- DFS augmenting path (기본): O(VE).
- Hopcroft-Karp: O(E√V), 레벨 그래프 + multi-path BFS.
- 최대 유량: 이분 매칭 = max-flow (L→R 용량 1). Dinic O(V^2 E) 또는 Push-Relabel O(V^3).
문제 상황과 동기
PS 전형: 직원-작업, 학생-프로젝트, 축사-소 배정.
- naive: 모든 간선 부분집합 (2^E) 순회 → 불가능.
- greedy: 임의 순서로 간선 추가 → 최대 보장 없음.
- augmenting path (Ford-Fulkerson 아이디어): 매칭을 키울 수 있는 경로를 DFS 로 찾기. O(VE).
- Hopcroft-Karp: BFS 로 여러 경로 한꺼번에 증강. O(E√V).
핵심 통찰: 증가 경로 (augmenting path) 존재 ↔ 매칭이 최대가 아님. Berge’s lemma.
실무: 추천 시스템, 작업 스케줄링, 네트워크 최적화.
시각화
핵심 아이디어
augmenting path
- 경로 P: 시작/끝 모두 미매칭 정점 (unmatched), 간선이 매칭/비매칭 교대.
- P 를 발견하면 매칭 간선 반전 (M ← M △ P) → |M| 증가 1.
- Berge’s lemma: augmenting path 없으면 M 이 최대 매칭.
DFS augmenting path (기본)
L 의 미매칭 정점에서 DFS:
- 비매칭 간선 (u, v) 로 R 진입.
- v 가 미매칭이면 성공 (경로 완성).
- v 가 이미 매칭되어 있으면 (v, w) 로 L 로 돌아가 재귀.
- 최악 O(VE): V 번의 DFS, 각 O(E).
Hopcroft-Karp (O(E√V))
- BFS 레벨 그래프 구축: 미매칭 L 에서 시작, 매칭/비매칭 교대로 레벨.
- DFS 로 여러 augmenting path 동시 증강.
- 레벨 그래프 갱신 반복.
- √V phase 내에 완료 (증명: 각 phase 마다 최단 augmenting path 길이 증가).
König 정리
이분 그래프에서:
|최대 매칭| = |최소 정점 덮개|.
- 정점 덮개 (vertex cover): 모든 간선이 적어도 한 끝점을 포함하는 정점 집합.
- 응용: 간선 선택 문제 = 정점 선택 문제 dual.
증명: 최대 매칭 M 에서 M-alternating path 로 덮개 구성.
Hall’s marriage theorem
L 의 모든 부분집합 S 에 대해 |N(S)| ≥ |S| (N(S) = S 의 이웃 집합 in R) ↔ L 의 완전 매칭 존재.
알고리즘
DFS augmenting path (기본)
bipartite_matching_dfs(G):
match_L[] = -1, match_R[] = -1
for u in L:
if match_L[u] < 0:
visited[] = false
dfs_augment(u)
return count(match_L >= 0)
dfs_augment(u):
for v in adj[u]:
if visited[v]: continue
visited[v] = true
if match_R[v] < 0 or dfs_augment(match_R[v]):
match_L[u] = v
match_R[v] = u
return true
return false
Hopcroft-Karp (O(E√V))
hopcroft_karp(G):
match_L[] = -1, match_R[] = -1
while bfs_level() < ∞:
for u in L:
if match_L[u] < 0:
dfs_augment_hk(u)
return count(match_L >= 0)
bfs_level():
queue = all unmatched u in L
level[u] = 0 for u in queue
min_level = ∞
while queue not empty:
u = queue.pop()
for v in adj[u]:
if match_R[v] < 0:
min_level = min(min_level, level[u] + 1)
elif level[match_R[v]] < 0:
level[match_R[v]] = level[u] + 2
queue.push(match_R[v])
return min_level
dfs_augment_hk(u):
for v in adj[u]:
if match_R[v] < 0:
match_L[u] = v
match_R[v] = u
return true
elif level[match_R[v]] == level[u] + 2 and dfs_augment_hk(match_R[v]):
match_L[u] = v
match_R[v] = u
return true
level[u] = -1 # 이번 phase 실패
return false
구현
// 이분 매칭 DFS augmenting path O(VE)
#include <bits/stdc++.h>
using namespace std;
int n, m, e;
vector<int> adj[1005]; // L[0..n-1] → R
int match_L[1005], match_R[1005];
bool visited[1005];
bool dfs_augment(int u) {
for (int v : adj[u]) {
if (visited[v]) continue;
visited[v] = true;
if (match_R[v] < 0 || dfs_augment(match_R[v])) {
match_L[u] = v;
match_R[v] = u;
return true;
}
}
return false;
}
int main() {
cin >> n >> m >> e; // L 크기 n, R 크기 m, 간선 e
fill(match_L, match_L + n, -1);
fill(match_R, match_R + m, -1);
for (int i = 0; i < e; i++) {
int u, v; cin >> u >> v; u--; v--; // 0-indexed
adj[u].push_back(v);
}
int cnt = 0;
for (int u = 0; u < n; u++) {
if (match_L[u] < 0) {
fill(visited, visited + m, false);
if (dfs_augment(u)) cnt++;
}
}
cout << "최대 매칭: " << cnt << "\n";
for (int u = 0; u < n; u++) {
if (match_L[u] >= 0) {
cout << u + 1 << " - " << match_L[u] + 1 << "\n";
}
}
}3 3 5
1 1
1 2
2 2
2 3
3 1최대 매칭: 3
1 - 2
2 - 3
3 - 1복잡도
| 항목 | 값 |
|---|---|
| DFS augmenting path | O(VE) 시간, O(V+E) 공간 |
| Hopcroft-Karp | O(E√V) 시간, O(V+E) 공간 |
| 최대 유량 (Dinic) | O(V^2 E) 시간 (이분 그래프에서 O(E√V)) |
| 전처리 | 없음 (그래프 입력만) |
König 정리와 최소 정점 덮개
이분 그래프에서:
- 최대 매칭 M: 간선 부분집합, disjoint endpoints.
- 최소 정점 덮개 C: 모든 간선이 C 의 정점 하나 이상 포함.
König 정리: |M| = |C|.
구성: 최대 매칭 M 에서 시작, M-alternating path 로 도달 가능한 정점 집합 Z 구하기.
- C = (L \ Z) ∪ (R ∩ Z).
- 증명: C 가 모든 간선 커버, |C| = |M|.
응용: “모든 간선을 덮는 최소 정점 개수” = “최대 매칭 크기” (이분 그래프 한정).
변형
- 가중치 이분 매칭 (Hungarian algorithm): 간선 가중치 최대/최소화. O(V^3).
- 완전 매칭 (perfect matching): |M| = min(|L|, |R|). Hall’s theorem.
- 최소 간선 덮개 (edge cover): 모든 정점을 덮는 최소 간선 집합. Gallai’s theorem: |edge cover| + |matching| = V.
- 최대 독립 집합 (maximum independent set): 이분 그래프에서 V - |최소 정점 덮개|.
함정
1. DFS visited 초기화
매 augmenting path 시도마다 visited[] 초기화 필수. 전역 visited 재사용하면 경로 놓침.
2. 미매칭 정점 체크
L 의 미매칭 정점에서만 DFS 시작. 이미 매칭된 정점에서 시작하면 무의미.
3. 양방향 간선 입력
이분 그래프는 L → R 단방향. 무방향 입력 시 L, R 분리 필요.
4. Hopcroft-Karp 레벨 그래프
레벨 초기화 타이밍 주의. 매 phase 마다 레벨 재계산.
BOJ 연습 문제
| 번호 | 제목 | 정답률 | 링크 |
|---|---|---|---|
| BOJ 11375 | 열혈강호 | - | kokoa-lab |
| BOJ 11376 | 열혈강호 2 (2배 용량) | - | kokoa-lab |
| BOJ 2188 | 축사 배정 | - | kokoa-lab |
| BOJ 1298 | 노트북의 주인을 찾아서 | - | kokoa-lab |
| BOJ 9577 | 토렌트 (시간 슬롯 + 이분 매칭) | - | kokoa-lab |
참고
이 글의 용어 (3개)
- 이분 그래프 (Bipartite Graph)algorithm
- 정의 이분 그래프 (Bipartite Graph) 는 정점 집합 V 를 두 개의 독립 집합 L, R 로 분할할 수 있고, 모든 간선이 L 과 R 을 잇는 그래프. 같은 집합 내 …
- Maximum Flow: Ford-Fulkerson, Edmonds-Karp, Dinicalgorithm
- 정의 Maximum Flow (최대 유량) 문제는 방향 그래프 $G = (V, E)$ 와 소스 $s$, 싱크 $t$ 가 주어졌을 때, 각 간선의 용량 제약 아래에서 $s$ 에서 …
- Minimum Vertex Cover: 최소 정점 덮개algorithm
- 정의 Vertex Cover 는 그래프의 모든 간선이 최소 한 정점을 포함하도록 하는 정점 집합. 최소 크기의 vertex cover 를 찾는 문제. - 일반 그래프: NP-ha…
이 개념을 다룬 위키 페이지 (10)
- wiki안정 결혼 문제 (Stable Marriage)
- wiki이분 그래프 (Bipartite Graph)
- wiki서큘레이션 (Circulation)
- wiki네트워크 유량 (Network Flow)
- wiki홀의 결혼 정리 (Hall's Marriage Theorem)
- wiki헝가리안 알고리즘 (Hungarian Algorithm)
- wikiMaximum Flow: Ford-Fulkerson, Edmonds-Karp, Dinic
- wiki최대 유량 최소 컷 정리 (Max-Flow Min-Cut Theorem)
- wikiMinimum Vertex Cover: 최소 정점 덮개
- wiki쌍대성 (Duality)
💬 댓글