분류: 시뮬레이션, BFS /
문제
문제 설명
N×N크기의 땅이 있고, 땅은 1×1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모든 나라는 1×1 크기이기 때문에, 모든 국경선은 정사각형 형태이다.
오늘부터 인구 이동이 시작되는 날이다.
인구 이동은 하루 동안 다음과 같이 진행되고, 더 이상 아래 방법에 의해 인구 이동이 없을 때까지 지속된다.
- 국경선을 공유하는 두 나라의 인구 차이가 L명 이상, R명 이하라면, 두 나라가 공유하는 국경선을 오늘 하루 동안 연다.
- 위의 조건에 의해 열어야하는 국경선이 모두 열렸다면, 인구 이동을 시작한다.
- 국경선이 열려있어 인접한 칸만을 이용해 이동할 수 있으면, 그 나라를 오늘 하루 동안은 연합이라고 한다.
- 연합을 이루고 있는 각 칸의 인구수는 (연합의 인구수) / (연합을 이루고 있는 칸의 개수)가 된다. 편의상 소수점은 버린다.
- 연합을 해체하고, 모든 국경선을 닫는다.
각 나라의 인구수가 주어졌을 때, 인구 이동이 며칠 동안 발생하는지 구하는 프로그램을 작성하시오.
입력
첫째 줄에 N, L, R이 주어진다. (1 ≤ N ≤ 50, 1 ≤ L ≤ R ≤ 100)
둘째 줄부터 N개의 줄에 각 나라의 인구수가 주어진다. r행 c열에 주어지는 정수는 A[r][c]의 값이다. (0 ≤ A[r][c] ≤ 100)
인구 이동이 발생하는 일수가 2,000번 보다 작거나 같은 입력만 주어진다.
출력
인구 이동이 며칠 동안 발생하는지 첫째 줄에 출력한다.
풀이
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct xy {int x, y;};
int N, L, R;
int board[50][50], vis[50][50];
int dx[] = {1, 0, -1, 0};
int dy[] = {0, 1, 0, -1};
bool migrate(int startX, int startY, int mark) {
int tot = 0;
vector<xy> nations;
queue<xy> Q;
Q.push({startX, startY});
vis[startX][startY] = mark;
while(not Q.empty()) {
const auto [x, y] = Q.front(); Q.pop();
for(int d=4; d--;) {
int nx = x + dx[d], ny = y + dy[d];
if(nx<0 or nx>=N or ny<0 or ny>=N) continue;
if(vis[nx][ny] == mark) continue;
int diff = abs(board[x][y] - board[nx][ny]);
if(diff < L or diff > R) continue;
vis[nx][ny] = mark;
Q.push({nx, ny});
tot += board[nx][ny];
nations.push_back({nx, ny});
}
}
if(nations.empty()) return false;
tot += board[startX][startY];
nations.push_back({startX, startY});
int pop = tot / nations.size();
for(const auto &[x, y]:nations) board[x][y] = pop;
return true;
}
bool findNewAssociation(int mark) {
bool isUpdated = false;
for(int x=0; x<N; x++) {
for(int y=0; y<N; y++) {
if(vis[x][y] == mark) continue;
if(migrate(x, y, mark)) isUpdated = true;
}
}
return isUpdated;
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
cin >> N >> L >> R;
for(int i=0; i<N; i++)
for(int j=0; j<N; j++)
cin >> board[i][j];
int day = 0;
while(findNewAssociation(day + 1)) day++;
cout << day;
}
- 새로운 연합이 없을 때까지 다음을 수행한다.(`while(findNewAssociation()) day++`)
- 새로운 연합을 찾는다.(`migrage()`의 BFS 부분)
- 새로운 연합이 있다면 이주한다.(`migrage()`의 `return false` 아랫 부분
위 풀이는 `L`의 최솟값이 0일 경우 틀린 풀이다.
`L`이 0도 가능하다면 한번 연합을 생성한 나라들은 항상 연합이되어 연합이 하나라도 있을 경우 `main()`의 while문은 무한루프에 빠진다.
이 경우 연합의 국경 나라들만 따로 저장해 처리해야할 것이다.
이 문제에서 함수는 크게 두개로 구분될 수 있다. `연합 찾기`, `인구 이동하기`
같은 날 연합이 여러개 생길 경우 모든 연합을 찾고 한번에 각 연합에 대해 인구 이동하는 것과 연합을 찾을 때마다 인구 이동을 하는 것은 엄연히 다른 결과를 갖는다.
이 문제에서는 모든 연합을 찾고 한번에 각 연합에 대해 인구 이동을 수행하는 것이 정확하다.
하지만 위 코드에서는 연합을 찾고 바로 인구 이동을 수행하는데 이는 `vis[][]`로 오늘 확인한 나라에 대해서는 다시 확인하지 않기 때문에 이전 연합에 포함되어 인구 이동이 수행되었던 나라는 다음 연합을 찾는 과정에서 배제되기 때문이다.
즉 한 나라에 대해 하루에 하나의 연합에만 포함되어 인구 이동이 한번만 수행되어야한다.
'Problem Solving > BOJ' 카테고리의 다른 글
[백준 - 11053] 가장 긴 증가하는 부분 수열 - C++, Python (0) | 2023.11.27 |
---|---|
[백준 - 11055] 가장 큰 증가하는 부분 수열 - C++ (0) | 2023.11.27 |
[백준 - 5373] 큐빙 - C++ (0) | 2023.11.24 |
[백준 - 1181] 단어 정렬 - C++ (0) | 2023.11.23 |
[백준 - 17281] ⚾ (야구) - C++ (0) | 2023.11.23 |