BOJ 16234 인구 이동
BOJ 16234 인구 이동
https://www.acmicpc.net/problem/16234
문제
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번 보다 작거나 같은 입력만 주어진다.
출력
인구 이동이 며칠 동안 발생하는지 첫째 줄에 출력한다.
예제 입력 1
2 20 50 50 30 20 40
예제 출력 1
1
예제 입력 2
2 40 50 50 30 20 40
예제 출력 2
0경계를 공유하는 나라의 인구 차이가 모두 L보다 작아서 인구 이동이 발생하지 않는다.
… 이하 예제 생략
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
static int N, L, R;
static boolean changed; // 인구 이동이 있는지 확인하기 위한 변수
static int[][] map;
static boolean[][] visited;
static int[] dirRow = {1, -1, 0, 0}, dirCol = {0, 0, 1, -1};
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
L = Integer.parseInt(st.nextToken());
R = Integer.parseInt(st.nextToken());
map = new int[N][N];
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < N; j++) {
map[i][j] = Integer.parseInt(st.nextToken());
}
}
int answer = -1;
do {
answer++;
changed = false; // 맨 처음 인구 이동이 없다는 의미로 변수를 false로 초기화
visited = new boolean[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if(!visited[i][j]) {
bfs(i, j);
}
}
}
} while (changed);
System.out.println(answer);
}
static void bfs(int row, int col) {
visited[row][col] = true;
int sum = 0, count = 0;
Queue<Point> queue = new LinkedList<>(); // 연합될 수 있는 칸을 bfs로 조사하기 위한 큐
Queue<Point> target = new LinkedList<>(); // 연합을 조사한 뒤 연합인 칸들의 인구 수를 평균으로 맞추기 위한 큐
Point start = new Point(row, col);
queue.offer(start);
target.offer(start);
while (!queue.isEmpty()) {
Point check = queue.poll();
// 방문한 칸의 인구를 총합에 더하고, 방문한 칸의 수를 1 추가
sum += map[check.x][check.y];
count++;
for (int i = 0; i < dirRow.length; i++) {
Point newPoint = new Point(check.x + dirRow[i], check.y + dirCol[i]);
boolean isOutside = 0 > newPoint.x || newPoint.x >= N || 0 > newPoint.y || newPoint.y >= N;
// 동서남북에 위치한 칸이 map 밖에 있거나 이미 방문한 칸일 경우, 생략
if(isOutside || visited[newPoint.x][newPoint.y]) continue;
// 인접한 지역의 인구차가 L 이상, R 이하일 경우, 연합 설정
int diff = Math.abs(map[check.x][check.y] - map[newPoint.x][newPoint.y]);
if(L <= diff && diff <= R) {
visited[newPoint.x][newPoint.y] = true;
queue.offer(newPoint);
target.offer(newPoint);
}
}
}
if(!changed && count > 1) changed = true; // 연합이 발생했을 경우, 인구 이동 플래그인 changed를 true로 설정
// 연합의 인구수 평균화 작업
int newPopulation = sum / count;
while (!target.isEmpty()) {
Point check = target.poll();
map[check.x][check.y] = newPopulation;
}
}
}
This post is licensed under CC BY 4.0 by the author.