https://www.acmicpc.net/problem/16234
16234번: 인구 이동
N×N크기의 땅이 있고, 땅은 1×1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모
www.acmicpc.net
해당 문제는 모든 지점에 대해 DFS 탐색을 진행하고, 연결된 요소에 대해서 값을 재할당한 뒤 다시 visited 배열을 초기화하여 처음부터 다시 탐색을 하는 방법을 사용해야 한다. 따라서 무한루프 내에서 DFS가 진행되며, 특정 조건이 되었을 때 빠져나올 수 있는 Flag가 필요하다.
- 기존 방식대로 DFS를 진행하되, 방문처리 이외에 Sum을 갱신하고, Vector에 해당 위치를 추가하는 작업을 해야 한다.
- 모든 지점에 대한 DFS 탐색이 끝난 뒤, 연합이 최소 한 개라도 존재한다면 방문배열을 초기화하고 DFS를 처음부터 다시한다.
- 모든 지점에 대한 DFS 탐색이 끝난 뒤, 연합이 단 한개도 존재하지 않을 경우 Flag를 갱신하여 무한루프를 종료한다.
- 구하는 값은 연합의 개수가 아닌 맵에 대한 모든 탐색이 끝났을 때, 연합의 유무를 통해 카운팅되기 때문에 출력되는 값 cnt는 이중 for문이 종료되었을 때 증가한다.
- 값이 올바르지 않을 경우, 중간중간 디버깅(cout을 통한 방문위치(y,x) 혹은 갱신된 인구수(이중 for문 - cout) 출력)을 통해 해결한다.
Key Point
- 기존 연결요소 DFS 문제와는 다르게 이미 방문했던 지점을 제외한 모든 지점을 방문해야 한다. 따라서 무한 루프인 while(true)를 사용하며, 중간에 빠져나올 수 있는 Flag 설정이 필요하다.
- DFS가 끝나면 연합인 지역의 인구를 다시 갱신해야 하므로, 해당 지역의 좌표 값을 저장해야 한다. 따라서 pair 형태로 Vector에 저장한다.
- 연합이 있으려면 최소 두 나라가 인구이동을 해야한다. 따라서 한 칸만 이동했을 경우는 연합이 있다고 계산하면 안된다. 즉, 연합이 있는 경우에만 Flag 값을 갱신한다.
문제 코드
#include <bits/stdc++.h>
using namespace std;
int n, L, R, arr[54][54], visited[54][54], cnt, sum;
const int dy[] = {-1, 0, 1, 0};
const int dx[] = {0, 1, 0, -1};
vector<pair<int, int>> v;
void DFS(int y, int x){
visited[y][x] = 1;
v.push_back({y,x});
sum += arr[y][x];
for(int i = 0; i < 4; i++){
int ny = y + dy[i];
int nx = x + dx[i];
if(nx < 0 || ny < 0 || nx >= n || ny >= n || visited[ny][nx]) continue;
if(abs(arr[ny][nx] - arr[y][x]) >= L && abs(arr[ny][nx] - arr[y][x]) <= R) DFS(ny,nx);
}
return ;
}
int main(){
cin >> n >> L >> R;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
cin >> arr[i][j];
}
}
while(true){
bool flag = false;
memset(visited, 0, sizeof(visited));
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(!visited[i][j]) {
sum = 0;
v.clear();
DFS(i,j);
if(v.size() == 1) continue;
for(pair<int, int> _pair : v) {
arr[_pair.first][_pair.second] = sum / v.size();
flag = true; // 연합이 있었다는 표시
}
}
}
}
if(!flag) break;
cnt++;
}
cout << cnt << "\n";
return 0;
}
- while문이 끝날 때마다(모든 지역에 대한 탐색이 끝났을 때) 방문배열과 Flag를 초기화한다. (완전탐색의 원.복)
- 특정 조건에 따라 방문을 진행하고 방문했을 때는 Sum에 합치고, Vector에 좌표를 저장한다.
- Vector의 경우, 하나의 연결요소에 대한 탐색이 끝났을 때 초기화가 되어야 한다.
- sum 또한 마찬가지로 하나의 연결요소에 대한 탐색이 끝났을 때 초기화가 되어야 한다.
디버깅을 습관화 하기
필자는 값이 올바르게 출력되지 않아 방문이 진행될 때마다 방문 지점의 좌표인 {y, x}를 출력하도록 하였다. 그 결과, 예상했던 지점을 방문하지 않는 것을 확인할 수 있었고, if문에서 abs()를 쓰지 않았음을 확인할 수 있었다. 또한 모든 지점에 대한 DFS가 끝나고, 모든 나라의 인구수를 출력하였을 때, 값이 0으로 나오는 문제를 발견하였다. sum을 0으로 초기화하는 과정에서 sum = 0이 아닌 int sum = 0으로 하여 새로운 지역변수를 할당하였음을 확인할 수 있었다.
이처럼 정확한 값이 나오지 않았을 때, 디버깅(편집기에서 디버깅 기능을 활용할 수 있으나 실제 코딩테스트 환경에 대비하기 위해 일일이 출력하여 결과를 확인)을 통해 문제를 해결하는 습관을 기르는 것이 코딩테스트 실력 향상에도 많은 도움이 되는 것 같다. 덕분에 해당 문제 및 DFS 탐색에 대한 이해도도 올라갔다.
10주완성 C++ 코딩테스트 | 알고리즘 코딩테스트 - 인프런 | 강의
네이버, 카카오, 삼성의 코딩테스트를 10주만에 합격시킨 최고의 코딩테스트 강의!, 코딩테스트, 이제 검증된 10주 완성 커리큘럼으로 정복하자!😎 [사진] 코딩테스트 강의어떤 것을 골라야 할까
www.inflearn.com
'CS > 알고리즘' 카테고리의 다른 글
백준 1992, 분할 정복 문제 (0) | 2023.08.12 |
---|---|
백준 2589, 모든 지점에 대한 최단거리를 구해야 하는 완전탐색(BFS) (0) | 2023.08.08 |
백준 15686, 2차원 좌표에 대한 조합을 활용한 완전탐색 문제 (0) | 2023.08.07 |
백준 1620, 두 개의 자료구조를 사용(+ 문자열을 숫자로 바꾸기) (0) | 2023.07.26 |
백준 1012, DFS 연결된 요소 찾기(+ 테스트 케이스 : 배열 초기화하기) (0) | 2023.07.26 |