본문 바로가기
알고리즘 문제/백준

[백준] 16234번: 인구 이동 (Java)

by Lpromotion 2024. 8. 21.

1. 문제설명

NxN 크기의 땅이 있고, 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 인구 이동은 하루동안 아래와 같이 진행되고, 더이상 인구 이동이 없을 때까지 지속된다.

각 나라의 인구수가 주어졌을 때, 인구 이동이 며칠 동안 발생하는지 구하는 문제이다.

  • 시간 제한: 2초
  • 메모리 제한: 512MB

 

2. 접근 방식

  • while문으로 인구 이동이 일어났는지 확인하고, 더 이상 인구 이동이 발생하지 않을 때까지 반복한다.
  • 매 반복 시작 시, 방문 여부 확인하는 배열(visited)를 초기화하고,
    모든 나라에 대해 방문하지 않은 경우 BFS 탐색한다.
  • 인접한 나라와의 인구 차이가 조건에 부합하면
    • 큐에 해당 위치를 넣고 방문 처리한다.
    • 연합 인구 계산을 위해 해당 위치를 별도의 큐에 저장한다.
    • “연합의 인구수”에 해당 나라의 인구수를 더하고, “연합을 이루고 있는 칸의 개수”를 1 증가시킨다.
  • 더 이상 조건에 부합하는 인접한 나라가 없다면(큐가 빌 경우), BFS 탐색을 종료한다.
    • 연합을 이루는 나라의 인구수를 (연합의 인구수)/(연합을 이루고 있는 칸의 개수)로 설정한다.
  • 인구 이동이 발생했을 경우에만 day를 증가시킨다.

 

3. 다른 풀이

https://moonsbeen.tistory.com/259

 

[백준]16234: 인구 이동 - JAVA

문제 https://www.acmicpc.net/problem/16234 16234번: 인구 이동 N×N크기의 땅이 있고, 땅은 1×1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고

moonsbeen.tistory.com

내가 푼 방식이 적절한 방식인지 확인하고 싶어서 다른 풀이를 찾아보았다.

전반적으로 비슷했는데, 조금 더 효율적인 방법을 찾았다.

나는 연합인 나라를 큐에 저장하고 연합인 나라의 개수를 BFS에서 증가시키는 방법을 사용했는데, 
참고한 풀이는 ArrayList로 연합인 나라의 위치를 저장하고, size()를 사용해서 평균값을 구했다.

나처럼 굳이 count 변수를 생성할 필요가 없는 방법이었다. 

작더라도 이런 효율적으로 코드를 작성할 수 있도록 노력해야겠다.

 

 

4. 최종 코드

import java.io.*;
import java.util.*;

public class Main {
    static int N, L, R;
    static int[][] country; // 각 나라의 인구 정보 저장
    static boolean[][] visited; // 방문 여부 확인

    // 상하좌우
    static int[] dx = {-1, 1, 0, 0};
    static int[] dy = {0, 0, -1, 1};

    static boolean bfs(int x, int y) {
        Queue<int[]> q = new ArrayDeque<>(); // BFS에 사용할 큐
        q.add(new int[]{x, y}); // 시작 위치 큐에 추가
        Queue<int[]> union = new ArrayDeque<>(); // 연합인 나라의 위치를 저장할 큐
        union.add(new int[]{x, y});
        visited[x][y] = true; // 시작 위치 방문 처리

        int population = country[x][y]; // 연합의 인구수
        int count = 1; // 연합을 이루고 있는 칸의 수

        while(!q.isEmpty()){
            int[] cur = q.poll(); // 큐에서 현재 위치 꺼냄
            for(int i=0; i<4; i++) { // 상하좌우로 인접한 칸 탐색
                int nx = cur[0] + dx[i];
                int ny = cur[1] + dy[i];
                // 땅의 범위 내에 있고, 아직 방문하지 않은 칸일 경우
                if(nx>=0 && nx<N && ny>=0 && ny<N && !visited[nx][ny]) {
                    // 인구 차이 계산
                    int absDif = Math.abs(country[cur[0]][cur[1]] - country[nx][ny]);
                    if(absDif>=L && absDif<=R) { // 인구 차이가 L이상 R이하일 경우
                        q.add(new int[]{nx, ny}); // 다음 탐색 위한 큐에 추가
                        visited[nx][ny] = true; // 방문 처리
                        union.add(new int[]{nx, ny}); // 연합 큐에 추가
                        population += country[nx][ny]; // 연합 총 인구수 갱신
                        count++; // 연합 이루는 칸의 수 증가
                    }
                }
            }
        }

        if(count > 1) { // 연합을 이루는 나라가 있다면
            int newPopulation = population/count; // 새로운 인구수 계산
            while(!union.isEmpty()) {
                // 연합에 속한 칸에 인구수 갱신
                int[] qq = union.poll();
                country[qq[0]][qq[1]] = newPopulation;
            }
            return true; // 인구 이동 일어남 (반환)
        }
        return false; // 인구 이동 일어나지 않음 (반환)
    }

    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());
        country = new int[N][N];
        for(int i=0; i<N; i++) { // 각 나라의 인구 정보 저장
            st = new StringTokenizer(br.readLine());
            for(int j=0; j<N; j++) {
                country[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        int day = 0; // 인구 이동이 발생한 날 수
        boolean check = true; // 인구 이동이 일어났는지 확인
        while(check) { // 인구 이동이 더이상 없으면 종료
            check = 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를 수행하고, 인구 이동이 발생하면 check를 true로 설정
                        if(bfs(i, j)) check = true;
                    }
                }
            }

            if(check) day++; // 인구 이동이 발생했으면 day를 증가
        }

        System.out.println(day);
    }
}

 

4. 느낀점 or 기억할정보

절대값을 구할 때 Math.abs()를 사용하면 된다.

BFS 탐색을 설계하는 것은 이제 익숙해진 것 같다. 하지만 BFS를 호출하는 것을 어떤 식으로 반복할지 생각하는 데 오래걸렸다.

이 문제는 인구 이동이 더 이상 발생해지 않을 때까지 반복을 통해 BFS를 호출해야 했다. bfs 메서드에서 인구 이동이 발생했는지 안했는지 반환하고, 그 반환값을 이용해 while문을 계속 실행할지 종료할지 설정하는 방법을 사용했다. 

반응형

댓글