(백준) 16234 인구 이동 – JAVA

  • by


출처 – 백준 알고리즘


알고리즘

NxN 크기의 2차원 배열 탐색, 하나의 정점을 기준으로 탐색하기 위해 DFS사용했다.

사각형에 인접한 국가 중 L 인구차 ≤R의 나라들끼리의 국경선이 사라져 하나의 연합국이 된다.

4방 탐색(상/하/좌/우)하여 연합국을 확장하고 더 이상 확장할 수 없는 경우 연합국의 인구이동을 수행한다.

✔️주의해야 할 점은 토지 한칸은 하루에 한 번만 탐색가능합니다.

즉, 인구이동이 발생한 토지는 다음날까지 인구이동이 더 이상 발생하지 않는다.


구현 알고리즘

이하의 과정을 더 이상 인구 이동이 일어나지 않을 때까지 반복한다.

(인구이동이 한번이라도 발생한 것을 확인하기 위한 플래그값 isChange 사용)

1. 탐색했는지(인구 이동이 발생했는지) 확인하기 위한 boolean형 2차원 배열의 생성

(1일 동안 이미 한 번 인구 이동이 발생한 경우 더 이상 탐색해야 합니다 x)

2. (아직 탐색하지 않은 토지라면) DFS 탐색을 시작한다.

  • 검색 처리(=방문 처리)
  • 큐에 대응하는 토지의 좌표 정보를 삽입합니다.

    (큐: 현재 연합을 이루고 있는 각국의 위치 정보)
  • 현재 연합을 이루고 있는 국가들의 총 인구수를 더한다.

  • 현재 위치(i, j)에서 사방 탐색진행합니다.

  • if 다음 네비게이션 포인트(ni, nj)가 NxN 크기의 토지 경계를 넘거나 이미 네비게이션한 곳인 경우, 다음 네비게이션 → continue
  • else 현재 (i, j)와 다음 (ni, nj)의 인구차가 L 이상 R 이하이면 (ni, nj)를 기준으로 DFS 탐색을 반복 (재귀)한다.

3-1. DFS 검색이 끝날 때 대기열 크기가 1이면 대기열을 비우고 다음 지점을 검색합니다.

(큐의 크기가 1 = 탐색을 시작한 나라에서 연합을 이룬 나라가 하나도 없다는 것 = 인구 이동 X)

3-2. 총 인구수/큐의 크기=연합을 이루고 있는 각 구간의 인구수

4. 대기열에 포함 된 연합국의 위치 정보를 취득하고, 연합국의 인구 수를 조정한다.


소스 코드

import java.util.*;
import java.io.*;
/*
 * 특정 땅에서 시작해서 dfs(깊이우선)로 탐색
 * 국경선 열 수 있는 경우, 큐에 넣음 
 * 탐색하다가 더 이상 국경선 열 수 없을 때 큐에 있는 것들 빼서 연합 
 * 큐에 있는 땅들 인구수 조절 
 * 그 다음 탐색 ㄱ */
public class Solution_BaekJoon_16234 {
	private static int dx()= {-1,1,0,0};
	private static int dy()= {0,0,-1,1}; 
	private static int N,L,R;
	private static int()() map;
	private static boolean()() visited;
	private static int sum;
	//하나의 땅에 대한 정보 
	static class Ground{
		//땅 위치(x,y)
		int x;
		int y;
		
		public Ground(int x, int y) {
			this.x = x;
			this.y = y;
		}
	}
	private static Queue<Ground> que= new LinkedList<>(); 
	
	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명 이상 R명 이하일 때, 국경선 열 수 있음 
		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 count =0; 
		
		while(true) {
		boolean isChange=false; 
		visited= new boolean(N)(N); 
		for(int i=0; i<N; i++) {
			for(int j=0; j<N; j++) {
				if(!
visited(i)(j)) { sum=0; dfs(i,j); // 탐색 시작한 거에서 더 연합 못한 경우 if(que.size() == 1) { que.poll(); continue; } isChange = true; //연합국 한 나라 인구수 int one_people=sum/que.size(); //인구 이동 while(!
que.isEmpty()) { Ground g= que.poll(); map(g.x)(g.y) = one_people; } } } } //인구 이동이 한번도 안일어났으면 종료 if(!
isChange) break; count ++; } System.out.println(count); }//end of main private static void dfs(int i, int j) { //방문체크하고, 큐에 넣고, 연합 인구수합에 더하고 visited(i)(j) = true; que.add(new Ground(i,j)); sum+=map(i)(j); for(int d=0; d<4; d++) { // 4방탐색 int ni = i+dx(d); int nj = j+dy(d); //땅 밖으로 나가거나, 이미 탐색 했던 곳 ( 하루에 동시 탐색하는 땅 x ) if(isOut(ni,nj) || visited(ni)(nj)) continue; //인접한 두 국가의 인구수 차이 int dif= Math.abs(map(i)(j)-map(ni)(nj)); if(dif>=L && dif<=R) { dfs(ni, nj); } } }//end of dfs private static boolean isOut(int i, int j) { return i<0 || i>=N || j<0 || j>=N; } }//end of class