Post

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.