코딩테스트 연습
[BOJ] 1707. 이분그래프 (bipartite_graph, dfs, bfs)
hio9_9
2022. 12. 27. 19:11
Gold IV
https://www.acmicpc.net/problem/1707
1707번: 이분 그래프
입력은 여러 개의 테스트 케이스로 구성되어 있는데, 첫째 줄에 테스트 케이스의 개수 K가 주어진다. 각 테스트 케이스의 첫째 줄에는 그래프의 정점의 개수 V와 간선의 개수 E가 빈 칸을 사이에
www.acmicpc.net
그래프의 정점의 집합을 둘로 분할하여, 각 집합에 속한 정점끼리는 서로 인접하지 않도록 분할할 수 있을 때, 그러한 그래프를 특별히 이분 그래프 (Bipartite Graph) 라 부른다.
그래프가 입력으로 주어졌을 때, 이 그래프가 이분 그래프인지 아닌지 판별하는 프로그램을 작성하시오.
input:
- 입력은 여러 개의 테스트 케이스로 구성되어 있는데, 첫째 줄에 테스트 케이스의 개수 K가 주어진다.
- 각 테스트 케이스의 첫째 줄에는 그래프의 정점의 개수 V와 간선의 개수 E가 빈 칸을 사이에 두고 순서대로 주어진다. 각 정점에는 1부터 V까지 차례로 번호가 붙어 있다.
- 이어서 둘째 줄부터 E개의 줄에 걸쳐 간선에 대한 정보가 주어지는데, 각 줄에 인접한 두 정점의 번호 u, v (u ≠ v)가 빈 칸을 사이에 두고 주어진다.
output:
- K개의 줄에 걸쳐 입력으로 주어진 그래프가 이분 그래프이면 YES, 아니면 NO를 순서대로 출력한다.
Idea :
- 이분그래프 인지 확인하는 방법
- tree이거나 even cycle 일 경우 이분그래프 → odd cycle (cycle을 잇는 edge의 개수가 홀수) 인지 확인
- 각 edge의 양 끝 vertex가 같은 그룹이 아니어야 함 → 각 edge의 양 끝 vertex의 그룹이 다른지 확인
Code 1: check odd cycle
- union-find를 이용해 cycle 여부를 확인
- cycle일 경우, bfs를 이용해 그래프를 따라가며 cycle을 잇는 edge의 개수를 확인
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
vector<int> parent;
vector<int> G[20000];
int my_find(int x) {
if (parent[x] == x) return x;
return parent[x] = my_find(parent[x]);
}
void my_union(int a, int b) {
int p_a = my_find(a), p_b = my_find(b);
(p_a < p_b) ? parent[b] = p_a : parent[a] = p_b;
}
bool check_cycle(int a, int b) {
if (my_find(a) == my_find(b)) return true;
return false;
}
bool check_Bipartite(int a, int b) {
if (!check_cycle(a, b)) return true;
queue<pair<int, int>> Q;
bool ch[20001] = {};
Q.push(make_pair(a, 1));
ch[a] = true;
while (!Q.empty()) {
int cur_v = Q.front().first;
int cur_cnt = Q.front().second;
Q.pop();
if (cur_v == b)
return (cur_cnt % 2 == 1) ? false : true;
for (auto e : G[cur_v]) {
if (ch[e]) continue;
Q.push(make_pair(e, cur_cnt+1));
ch[e] = true;
}
}
}
int main() {
ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
// freopen("input.txt", "rt", stdin);
int K;
cin >> K;
while (K--) {
/* init */
parent.clear();
for (int i=0; i<=20000; i++)
G[i].clear();
/* input */
int V, E;
cin >> V >> E;
for(int i=0; i<=V; i++)
parent.push_back(i);
/* solve */
int a, b;
bool isBipartite = true;
while (E--) {
cin >> a >> b;
if (!isBipartite) continue;
if (!check_Bipartite(a, b))
isBipartite = false;
G[a].push_back(b);
G[b].push_back(a);
my_union(a, b);
}
/* print answer */
(isBipartite) ? cout << "YES\n" : cout << "NO\n";
}
}
Code 2: check vertcies' group of each edge
- bfs를 이용해 edge로 연결되어 있는 모든 vertecies의 그룹을 확인
- 현재 vertex와 edge로 연결되어 있는 모든 vertex의 그룹을 확인
- 같은 그룹이면 이분그래프가 아님, 더 이상 체크할 필요가 없으므로 빠져나옴
- 다른 그룹이면 아직까진 이분그래프임
- 아직 그룹이 특정되지 않았으면 현재 vertex의 그룹과 다른 그룹으로 지정
- 현재 vertex와 연결되어 있는 모든 vertecies에 대해 위 동작을 반복
- 이분그래프가 아님을 확인하여 중간에 빠져나오지 않고 모든 vertecies의 그룹을 확인했다면 이분그래프임
- 현재 vertex와 edge로 연결되어 있는 모든 vertex의 그룹을 확인
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
#define INIT 0
#define A 1
#define B -1
/* solve: idx vertex가 속한 그래프가 이분그래프인지 확인하는 함수
*
* parameters - G: 그래프 정보
* - color: 각 vertex의 그룹(색) 정보
* return - 이분그래프이면 true, 아니면 false
*/
bool solve(vector<vector<int>> G, vector<int> &color, int idx) {
queue<int> Q;
Q.push(idx);
color[idx] = A;
while (!Q.empty()) {
int cur_i = Q.front(); Q.pop();
for (auto e : G[cur_i]) {
if (color[cur_i] == color[e]) // edge의 양 끝 vertex가 같은 그룹에 속함
return false;
else if (color[e] == INIT) { // INIT일 경우 이전에 그룹 체크를 하지 않았음을 의미, bfs의 체크배열의 기능 겸함
color[e] = -color[cur_i];
Q.push(e);
}
}
}
return true;
}
int main() {
ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
// freopen("input.txt", "rt", stdin);
int K;
cin >> K;
while (K--) {
/* input */
int V, E, v1, v2;
cin >> V >> E;
vector<vector<int>> G(V+1);
vector<int> color(V+1, INIT);
while (E--) {
cin >> v1 >> v2;
G[v1].push_back(v2);
G[v2].push_back(v1);
}
/* solve */
bool isBipartite = true;
// 하나의 연결그래프가 아닐 경우, 모든 그래프를 확인하기 위함
for (int i=1; i<=V; i++) {
if (color[i] == INIT) {
isBipartite = solve(G, color, i);
if (!isBipartite) break;
}
}
/* print answer */
(isBipartite) ? cout << "YES\n" : cout << "NO\n";
}
Result :
Code 1) 시간초과
Code 2) 메모리: 7896 KB, 시간: 1920 ms
Review :
- union - find를 이용해 풀 수 있을 것이라고 생각했으나 시간, 메모리 초과...
- 논리적으로는 맞는 것 같은데 odd cycle인지 확인하는 부분이 문제이지 않을까...
- bfs를 이용하는 방법도 생각보다 너무 많은 시간이 소요됨
- 더 효과적인 방법을 찾아보자!