[백준] 16234 인구 이동 JavaScript

·2025년 3월 6일

문제

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번 보다 작거나 같은 입력만 주어진다.

출력

인구 이동이 며칠 동안 발생하는지 첫째 줄에 출력한다.

예제 입력

2 20 50
50 30
20 40

예제 출력

1

내가 했던 풀이 방법

  1. count(인구 이동이 일어난 일수)를 0으로 초기화한다.
  2. 매일 visited를 초기화해주고 isOpen(오늘 인구 이동을 위해 국경선을 오픈했는지)을 false로 초기화해준다.
  3. 모든 나라를 체크하면서 방문하지 않은 나라를 BFS를 통해 방문한다. 이때 BFS에서는 인접한 나라들 중 두 나라의 인구 차이가 L명 이상, R명 이하인 나라들을 기준으로 방문한다. 이때, 방문한 나라들을 route에 담아주고, 방문한 나라의 인구수를 sum에 더해준다. 인접한 모든 가능한 나라를 방문했을 때 route의 length로 sum을 나눠주면 연합의 새로운 인구수가 된다. route에 담긴 나라들을 모두 average로 인구를 변경해준다. 이와 같이 인구 이동이 진행되었다면 true를 그렇지 않았다면 false를 반환한다.
  4. BFS 이후 반환된 결과가 true인 경우, 인구이동이 최소 한 번은 일어난 것이기 때문에 isOpen을 true로 바꿔준다. 모든 나라에 대해 방문을 했을 때 isOpen이 true인 경우, 인구 이동이 발생했으므로 count를 1 증가시켜주고 이를 다시 반복한다. 만약 인구이동이 발생하지 않았다면 반복문을 종료하고 count를 출력한다.

코드

const fs = require('fs');
let [num, ...people] = fs.readFileSync(0, 'utf-8').toString().trim().split('\n');

let [N, L, R] = num.trim().split(' ').map(Number);
people = people.map((i) => i.trim().split(' ').map(Number));

let move = [
  [0, 1],
  [0, -1],
  [1, 0],
  [-1, 0],
];

let visited = Array.from({ length: N }, () => Array(N).fill(false));

function BFS(start) {
  let queue = [start];
  visited[start[0]][start[1]] = true;
  let route = [];

  let sum = 0;
  while (queue.length) {
    let [idY, idX] = queue.shift();
    sum += people[idY][idX];
    route.push([idY, idX]);

    for (let i = 0; i < 4; i++) {
      let newY = idY + move[i][0];
      let newX = idX + move[i][1];

      if (newY >= 0 && newY < N && newX >= 0 && newX < N && !visited[newY][newX]) {
        let diff = Math.abs(people[idY][idX] - people[newY][newX]);
        if (L <= diff && diff <= R) {
          queue.push([newY, newX]);
          visited[newY][newX] = true;
        }
      }
    }
  }

  if (route.length > 1) {
    let average = Math.floor(sum / route.length);

    for (let i = 0; i < route.length; i++) {
      people[route[i][0]][route[i][1]] = average;
    }
    return true;
  } else return false;
}

let count = 0;
while (true) {
  visited = Array.from({ length: N }, () => Array(N).fill(false));
  let isOpen = false;
  for (let i = 0; i < N; i++) {
    for (let j = 0; j < N; j++) {
      if (!visited[i][j]) {
        let result = BFS([i, j]);
        if (result) isOpen = true;
      }
    }
  }
  if (isOpen) count++;
  else break;
}

console.log(count);

회고

연합 부분을 놓쳐서 구현으로 풀이했다가 TC랑 답이 달라서 삽질을 했던 문제... 연합인 걸 확인하니 BFS로 풀어야 하는 게 뚜렷하게 보여서 BFS로 풀이를 바꿨더니 바로 정답이었다..ㅎ 사실 구현도중에도 BFS랑 유사하게 풀이가 흘러가네~ 했으면서 BFS라는 의심을 전혀 안 가진게 너무 어이없음..ㅎ

profile
Frontend🍓

0개의 댓글