Skip to content

Commit fdd1369

Browse files
committed
feat: 이분매칭(BipartiteMatching)관련 글 추가
1 parent 6f58503 commit fdd1369

File tree

4 files changed

+143
-0
lines changed

4 files changed

+143
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
---
2+
title: "이분매칭(Bipartite Matching)"
3+
date: 2024-03-07
4+
# toc: true
5+
categories:
6+
- algorithm
7+
tags:
8+
- algo
9+
- math
10+
---
11+
12+
두 그룹이 있을 때, 각 그룹 노드 들이 모두 서로 다른 매칭(연결)에 속하게 만들 수 있는 그래프를 이분 그래프(Bipartite Graph) 라고 한다.
13+
14+
15+
<br>
16+
17+
이분 매칭은 두 그룹을 이분 그래프와 같이 매칭해나가는 알고리즘이다.
18+
19+
<br>
20+
21+
주로 사람에게 일을 1개씩 부여해서 최대한 모든 사람이 일할 수 있는 방법을 찾거나, 소개팅같이 1-1로 매칭할때 최대한 모든 사람이 매칭되도록 하는 등… 결론은 두 그룹으로 나눌 수 있고, 연결(매칭)을 지을 수 있다면 이분 매칭을 통해 최대한 두 그룹을 매칭시켜줄 수 있다.
22+
23+
24+
<br>
25+
26+
하지만, 이 역시 그래프이기 때문에 그래프와 관련된 다양한 알고리즘이 사용될 수 있다.
27+
28+
29+
<br>
30+
31+
### 이분 매칭은 어떻게 이뤄지나?
32+
33+
이분 매칭은 몇가지 방법이 있지만, 그 중 가장 이해하기 쉬운건 DFS 를 기반으로 한다. 간단한 예시로 열형강호 문제를 기반으로 하는 경우, 현재 노드가 연결하고자 하는 다른 그룹의 노드가 이미 연결된 경우, DFS 를 통해서 기존 연결을 변경하여서 연결수를 늘릴 수 있을 지를 확인한다. 아래 그림을 확인하자.
34+
35+
<p align="center">
36+
<img width="600" src="/assets/images/BipartiteMatching-01.png" >
37+
</p>
38+
<p align="center">
39+
<img width="600" src="/assets/images/BipartiteMatching-02.png" >
40+
</p>
41+
<p align="center">
42+
<img width="600" src="/assets/images/BipartiteMatching-03.png" >
43+
</p>
44+
45+
46+
47+
<br>
48+
49+
기존 목표는 두 그룹을 최대한 많이 연결을 하는 것이다. 2번 차례가 되어 연결을 하려 할 때, 기존 1-a 의 연결을 유지하는 것보다, 1의 연결을 변경하는 것이 더 좋다는 것은 직관적으로 알 수 있다. 각 노드별로 탐색하기에 시간복잡도는 아래와 같다.
50+
51+
<br>
52+
53+
**시간 복잡도 : O(V*E^2)**
54+
- V : 간선(연결 수)
55+
- E : 노드의 수 (그룹 대상의 수)
56+
57+
<br>
58+
59+
이런 일련의 과정을 DFS와 같이 보게 된다면… 최대 연결을 지으려고 할 때, 2번 → a → 1번 에게 각각 전파하면서 다른 연결을 지을 수 있는지 탐색하게 된다. 이렇게 최대 연결을 늘리는 경로를 ‘증가 경로’라고 부른다.
60+
61+
62+
<br>
63+
64+
> 어어..? 잠시만요.. 뭔가.. 떠오르는 거 같기도 한데… 혹시 포드 풀커슨?!
65+
66+
67+
<br>
68+
69+
맞다.. 위에서 말했듯이 그래프로 이뤄지기 때문에… 각 그룹에 시작점, 마지막 점을 연결하면 최대유량 문제와 동일하게 된다. 단지 각 연결의 값이 1이라는 점 빼고…! 따라서, DFS 외에 BFS 로 탐색하여 증가 경로를 찾을 수 있고, 시간복잡도를 더 줄이는 다른 방식도 있다.
70+
71+
<br>
72+
73+
포드 풀커슨, 에드몬트 카프....
74+
75+
```java
76+
// boolean[] c //매칭 여부
77+
// ArrayList<ArrayList<Integer>> t //그래프
78+
// int[] bb //매칭된 노드 번호 저장 배열
79+
80+
//매칭 성공 = true return.
81+
public static boolean dfs(int x) {
82+
for(int i=0; i<aa.get(x).size(); i++) {
83+
int t=aa.get(x).get(i);
84+
if(c[t]) {continue;} //이미 매칭된 경우 pass
85+
c[t]=true;
86+
//매칭될 것이 있는 경우
87+
if(bb[t]==0||dfs(bb[t])) {bb[t]=x; return true;}//매칭
88+
}
89+
return false;
90+
}
91+
92+
// 전체 코드
93+
94+
public class Main {
95+
96+
public static boolean[] c=new boolean[202];
97+
public static ArrayList<ArrayList<Integer>> aa=new ArrayList<ArrayList<Integer>>();
98+
public static int[] bb=new int[202];
99+
100+
public static boolean dfs(int x) {
101+
for(int i=0; i<aa.get(x).size(); i++) {
102+
int t=aa.get(x).get(i);
103+
if(c[t]) {continue;}
104+
c[t]=true;
105+
if(bb[t]==0||dfs(bb[t])) {bb[t]=x; return true;}
106+
}
107+
return false;
108+
}
109+
public static void main(String[] args) {
110+
Scanner sc=new Scanner(System.in);
111+
int N=sc.nextInt(); int M=sc.nextInt();
112+
113+
for(int i=0; i<=N ;i++) {
114+
aa.add(new ArrayList<Integer>());
115+
}
116+
for(int i=1; i<=N; i++) {
117+
int a=sc.nextInt();
118+
for(int j=0; j<a; j++) {
119+
int b=sc.nextInt();
120+
aa.get(i).add(b);
121+
}
122+
}
123+
124+
int count=0;
125+
for(int i=1; i<=N; i++) {
126+
c=new boolean[202];
127+
if(dfs(i)) {count++;}
128+
}
129+
System.out.println(count);
130+
131+
}
132+
}
133+
```
134+
135+
136+
137+
**추천 문제**
138+
139+
문제로는 [11375: 열혈강호](https://www.acmicpc.net/problem/11375)를 추천한다.
140+
141+
<br>
142+
143+
43.5 KB
Loading
47.9 KB
Loading
53.6 KB
Loading

0 commit comments

Comments
 (0)