berry

[BOJ] 16234 (인구이동) - Java 본문

Problem Solving/BOJ

[BOJ] 16234 (인구이동) - Java

berryiscute 2022. 5. 4. 10:49
반응형

 

https://www.acmicpc.net/problem/16234

 

16234번: 인구 이동

N×N크기의 땅이 있고, 땅은 1×1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모

www.acmicpc.net

[풀이]

국경선이 열리는 조건은 다음과 같다.

  • 국경선을 공유하는 두 나라의 인구 차이가 L명 이상, R명 이하라면, 두 나라가 공유하는 국경선을 오늘 하루 동안 연다.
  • 위의 조건에 의해 열어야하는 국경선이 모두 열렸다면, 인구 이동을 시작한다.
  • 국경선이 열려있어 인접한 칸만을 이용해 이동할 수 있으면, 그 나라를 오늘 하루 동안은 연합이라고 한다.
  • 연합을 이루고 있는 각 칸의 인구수는 (연합의 인구수) / (연합을 이루고 있는 칸의 개수)가 된다. 편의상 소수점은 버린다.
  • 연합을 해체하고, 모든 국경선을 닫는다.

1. 국경선을 공유한다 -> 이 말의 뜻은 그래프탐색에서 흔히 나오는 상하좌우로 연결되어있는 좌표란 뜻이다.(대각선제외)

 

2. 국경선이 모두 열렸다면 인구이동을 시작한다 -> 하나의 나라로 부터 상하좌우 체크를 해서 L <= 인구수 <= R 인 나라가 있다면 연합인 나라리스트에 추가해주고 인구 이동을 시작해주면 된다.

 

3. 연합을 해체하고, 모든 국경선을 닫는다 -> 연합리스트를 비워주고 다시 인구체크를 하면서 추가해주면 된다.

 

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int n, l, r, days, totalPopulTmp, totalAssoTmp;
    static int[][] map;
    static int[] dx = {-1, 1, 0, 0};
    static int[] dy = {0, 0, -1, 1};
    static boolean[][] visited;
    static boolean isOpen;		//국경선이 열리는지 닫히는지 체크
    static public class Nation{	//각 나라의 좌표값(x, y)과 인구수를 담아줄 클래스
        int x;
        int y;
        int popul;
        Nation(int x, int y, int popul){
            this.x = x;
            this.y = y;
            this.popul = popul;
        }
    }
    static List<Nation> Asso;		//연합인 나라들을 담아줄 리스트
    public static void main(String[] args) throws Exception {
        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());

        isOpen = false;
        map = new int[n][n];
        days = 0;
        Asso = new ArrayList<>();

        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());
            }
        }

        while(true){		//몇 일이 걸리는지 확인하기 위한 flag값으로
            isOpen = false;	//종료 조건을 계속 초기화시켜줌
            visited = new boolean[n][n];
            for(int i=0;i<n;i++){
                for(int j=0;j<n;j++){
                //연합을 시킬 기준이되는 나라 고르기(아무거나)
                //단, 기준이 되는 나라에서 bfs진행하게되면
                 //방문체크도 같이 하기때문에 방문하지 않은 나라여야함
                    if(!visited[i][j]) {      
                        bfs(i, j);			  
                    }						 
                }
            }
            //더이상 국경선이 열리지 않는 경우에는 종료.
            if(!isOpen) break;				
            days++;
        }
        System.out.println(days);
    }
	
    //두 나라의 인구수 차이가 l이상 r이하일 때 true
    public static boolean open(int Nation1, int Nation2){           
        if(Math.abs(Nation1 - Nation2) <= r && l <= Math.abs(Nation1 - Nation2)) return true;
        return false;
    }

    public static void bfs(int x, int y){
        totalAssoTmp = 0;				//연합인 나라의 수
        totalPopulTmp = 0;				//연합인 나라의 총 인구수
        Queue<Nation> q = new LinkedList<>();
        q.offer(new Nation(x, y, map[x][y]));
        visited[x][y] = true;
        //현재 나라를 연합 리스트에 추가하고
        Asso.add(new Nation(x, y, map[x][y]));
        //현재 나라를 기준으로 국경선을 열 수 있는 나라구하기
        while(!q.isEmpty()){								
            Nation cur = q.poll();
            int cx = cur.x;
            int cy = cur.y;
            for(int i=0;i<4;i++){
                int nx = cx+dx[i];
                int ny = cy+dy[i];
                if(nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
                //인구수가 l 이상 r 이하인 나라를 찾게되면
                if(open(map[cx][cy], map[nx][ny]) && !visited[nx][ny]) {             
                    isOpen = true;
                    visited[nx][ny] = true;
                    //연합에 추가되는 나라들도 넣어준다
                    Asso.add(new Nation(nx, ny, map[nx][ny]));                   
                    q.offer(new Nation(nx, ny, map[nx][ny]));
                }
            }
        }
        
        //연합 나라들의 인구수 총합
        for(Nation n : Asso){
            totalPopulTmp += n.popul;           
        }
		
        //연합나라의 인구수 총합 / 연합나라 수
        int changePopul = calc(totalPopulTmp, Asso.size());		
		
        //2중 for문 돌리게 되면 시간초과가 날 수도 있으니까
        //미리 만들어놓은 리스트에서 필요한 좌표값만 꺼내쓰면됨!
        for(int i=0;i<Asso.size();i++){						
            int changeX = Asso.get(i).x;					
            int changeY = Asso.get(i).y;
            map[changeX][changeY] = changePopul;
        }
		
        //인구이동을 끝마치면 연합리스트를 비워줘야 다음 인구이동에 영향을 미치지 않는다
        Asso.clear();										

    }
    
    public static int calc(int totalPopul, int totalAsso){   
        return totalPopul / totalAsso;
    }
}

 

반응형

'Problem Solving > BOJ' 카테고리의 다른 글

[BOJ] 아기 상어 - 16236(Java)  (4) 2022.08.30
[BOJ] 압축 - 1662 (Java)  (0) 2022.07.25
[BOJ] 연구소 3 - 17142 (Java)  (0) 2022.05.13
[BOJ] 17141 (연구소2) - Java  (0) 2022.04.29
[BOJ] 2156 (포도주 시식) - Java  (2) 2022.03.31