2023. 5. 1. 14:14ㆍIT이모) 알고리즘 문제풀이/백준 문제풀이
https://www.acmicpc.net/problem/1976
1976번: 여행 가자
동혁이는 친구들과 함께 여행을 가려고 한다. 한국에는 도시가 N개 있고 임의의 두 도시 사이에 길이 있을 수도, 없을 수도 있다. 동혁이의 여행 일정이 주어졌을 때, 이 여행 경로가 가능한 것인
www.acmicpc.net
백준 사이트 1976번 문제, 여행 가자 문제다.
본인은 C++로 문제풀이를 진행하였다.
0. 문제 읽기
- 여행 계획이 가능한 경우 "YES"를 출력하고, 불가능한 경우 "NO"를 출력해야 한다.
- 또한, 여행 계획이 가능하려면 여행 경로상의 모든 도시가 서로 연결되어 있어야 한다.
1. 문제풀이 아이디어
- 여행 경로상의 모든 도시가 서로 연결되어 있는지 확인하는 것은 유니온 파인드(Union-Find) 알고리즘을 이용하면 간단히 구현할 수 있다.
- 먼저, 입력받은 도시들의 연결 여부를 기반으로 유니온 파인드를 실행하여 모든 도시들을 연결한다.
- 그리고나서, 주어진 여행 계획에 있는 도시들의 루트가 모두 같은지 확인하여 모든 도시들이 서로 연결되어 있는지를 판단한다.
2. 코드 핵심 부분
1) Union-Find 자료구조를 구현한 함수,
find 함수는 처음보면 이해가 잘 되지 않을 수도 있다.
부모 노드를 재귀적으로 자신을 부모로 갖는 노드까지 찾아 올라가고,
그 노드를 반환한다.
merge 함수는 A노드와 B노드를 통합시켜주는 역할을 한다.
이 문제에서 각 도시의 연결이 인접배열 형태로 주어지기 때문에,
연결된 도시의 경우에 merge함수에 넣어 같은 집합으로 통합해주면 된다.
// Union-Find 자료구조에서 노드 now의 루트 노드를 반환하는 함수
int find(int now) {
if (Parent[now] == now) // 현재 노드의 부모 노드가 자기 자신이라면, 현재 노드가 루트 노드임
return now;
return Parent[now] = find(Parent[now]); // 부모 노드를 재귀적으로 찾아감 (경로 압축)
}
// Union-Find 자료구조에서 A와 B를 합치는 함수
void merge(int A, int B) {
int PA = find(A); // A의 루트 노드를 찾음
int PB = find(B); // B의 루트 노드를 찾음
if (PA == PB) // A와 B가 이미 같은 집합에 속해있다면, 합치지 않음
return;
Parent[PB] = PA; // B의 루트 노드의 부모 노드를 A의 루트 노드로 설정하여 A와 B를 합침
}
2) 위에서 구현한 Union-Find 자료구조 함수를 실제로 활용하는 부분이다.
merge 함수를 실행시켜 연결 정보대로 도시들을 통합시켜 주고,
여행 계획의 도시들이 연결되어 있는지 확인하는 과정이다.
그런데 내가 여기서 처음에 실수를 했는데,
if (find(Plan[i]) != find(Plan[i - 1])) 이 부분을,
if (Parent[Plan[i]] != Parent[Plan[i - 1]])로 실행하였었다.
위에서 통합과정을 거쳤기에 Parent에 그 정보가 모두 반영되어 통합되어 있을 것이라 생각했는데,
아니었다.
다시 한번 find로 각 도시의 최종 부모를 찾아 올라가서 그 부모를 비교해야
정확한 답이 도출된다.
// 여행 계획이 가능한지 여부를 판단하는 함수
void solve() {
// Union-Find 자료구조를 이용하여, 모든 연결 정보를 합침
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
if (Map[i][j] == 0)
continue;
merge(i, j);
}
}
// 여행 계획에 속한 도시들이 모두 같은 집합에 속하는지 확인
for (int i = 1; i < M; i++) {
// 여행 계획이 불가능한 경우
if (find(Plan[i]) != find(Plan[i - 1])) {
Ans = "NO";
return;
}
}
// 여행 계획이 가능한 경우
Ans = "YES";
}
3. 전체 코드
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
// 필요 인자들
int N, M; // 도시의 수와 여행 계획에 속한 도시의 수
int Map[201][201]; // 도시들 간 연결 정보를 저장하는 이차원 배열
int Plan[1000]; // 여행 계획에 속한 도시들을 저장하는 배열
int Parent[201]; // Union-Find 자료구조를 위한 부모 노드를 저장하는 배열
string Ans; // 여행 계획이 가능한지 여부를 저장하는 문자열
// 값 입력받는 함수
void input() {
cin >> N >> M;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
cin >> Map[i][j]; // 도시들 간 연결 정보를 입력받음
}
}
for (int i = 0; i < M; i++) {
cin >> Plan[i]; // 여행 계획에 속한 도시들을 입력받음
}
for (int i = 1; i <= N; i++) {
Parent[i] = i; // 초기에는 모든 도시의 부모 노드는 자기 자신으로 설정
}
}
// Union-Find 자료구조에서 노드 now의 루트 노드를 반환하는 함수
int find(int now) {
if (Parent[now] == now) // 현재 노드의 부모 노드가 자기 자신이라면, 현재 노드가 루트 노드임
return now;
return Parent[now] = find(Parent[now]); // 부모 노드를 재귀적으로 찾아감 (경로 압축)
}
// Union-Find 자료구조에서 A와 B를 합치는 함수
void merge(int A, int B) {
int PA = find(A); // A의 루트 노드를 찾음
int PB = find(B); // B의 루트 노드를 찾음
if (PA == PB) // A와 B가 이미 같은 집합에 속해있다면, 합치지 않음
return;
Parent[PB] = PA; // B의 루트 노드의 부모 노드를 A의 루트 노드로 설정하여 A와 B를 합침
}
// 여행 계획이 가능한지 여부를 판단하는 함수
void solve() {
// Union-Find 자료구조를 이용하여, 모든 연결 정보를 합침
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
if (Map[i][j] == 0)
continue;
merge(i, j);
}
}
// 여행 계획에 속한 도시들이 모두 같은 집합에 속하는지 확인
for (int i = 1; i < M; i++) {
// 여행 계획이 불가능한 경우
if (find(Plan[i]) != find(Plan[i - 1])) {
Ans = "NO";
return;
}
}
// 여행 계획이 가능한 경우
Ans = "YES";
}
// 메인함수
int main() {
// input
input();
// solve
solve();
// output
cout << Ans;
return 0;
}
4. 문제 풀이를 마치며..
백준에서 내가 처음으로 풀어본 Union-Find 알고리즘 문제이다.
사실 이 알고리즘을 학습한건 2월의 SSAFY 수업이었으니 한참지난 이야긴데,
왜 이제서야 처음으로 풀었냐면..
이 알고리즘 수업날 졸업식 때문에 교육을 빠졌었다 ㅠㅠ
그래서 복습을 미루고 미루다가 이제야 겨우 공부하게 되었다.
삼성 SW 역량테스트 B형을 처음으로 시험보게 되었었는데
그 대비를 하며 미뤄놨던 알고리즘들을 하나씩 공부하는 중이다.
그에 따라 나의 백준 문제풀이도 다양해지고,
그만큼 블로그에 포스팅할 알고리즘들도 다양해질 것이라 기대된다.
'IT이모) 알고리즘 문제풀이 > 백준 문제풀이' 카테고리의 다른 글
[BOJ][백준 13549] 숨바꼭질 3 - C++ (0) | 2023.05.10 |
---|---|
[BOJ][백준 2668] 숫자고르기 - C++ (0) | 2023.05.09 |
[BOJ][백준 7576] 토마토 - C++ (2) | 2023.04.17 |
[BOJ][백준 1038] 감소하는 수 - C++ (0) | 2023.04.14 |
[BOJ][백준 15486] 퇴사 2 - C++ (0) | 2023.04.13 |