백준 17472번 JAVA : 다리만들기2

문제

백준 문제 링크 : https://www.acmicpc.net/problem/17472

풀이전략

  • DFS를 사용하여 섬의 연결정보를 얻는다.
  • 각 섬들을 연결하는 모든 다리를 구한다. (N과 M은 10이하이므로 시간은 충분하다.)
  • 각 다리들을 priorityQueue에 저장하여 최소길이부터 뽑으면서 섬들을 연결해준다.

코드

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

public class Main {
	static int N, M;
	static int islandCnt;
	static int[] island;
	static int[][] board;
	static int[] dy = {-1, 0, 1, 0};
	static int[] dx = {0, 1, 0, -1};


	public static void DFS(int r, int c) {
		for(int i=0; i<4; i++) {
			int rr = r + dy[i];
			int cc = c + dx[i];

			if(rr > N || rr < 1 || cc > M || cc < 1) continue;
			if(board[rr][cc] == 1) {
				board[rr][cc] = islandCnt;
				DFS(rr,cc);
			}

		}
	}

	static class Road{
		public int land1, land2, dis;
		public Road(int land1, int land2, int dis) {
			this.land1 = land1;
			this.land2 = land2;
			this.dis = dis;
		}
	}

	public static boolean checkIsland() {
		for(int i=2; i<=islandCnt; i++) {
			if(island[i] != island[2] || island[2]==0) return false;
		}
		return true;
	}

	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());
		M = Integer.parseInt(st.nextToken());

		board = new int[N+1][M+1];

		for(int i=1; i<=N; i++) {
			st = new StringTokenizer(br.readLine());
			for(int j=1; j<=M; j++) {
				board[i][j] = Integer.parseInt(st.nextToken());
			}
		}

		// 섬의 개수 및 섬의 종류 체크
		islandCnt = 1;
		for(int i=1; i<=N; i++) {
			for(int j=1; j<=M; j++) {
				if(board[i][j] == 1) {
					islandCnt++;
					board[i][j] = islandCnt;
					DFS(i,j);
				}
			}
		}


		// priorityQueue로 섬 연결정보 저장
		PriorityQueue<Road> pq = new PriorityQueue<>((r1, r2)-> r1.dis - r2.dis);

		for(int i=1; i<=N; i++) {
			for(int j=1; j<=M; j++) {
				if(board[i][j] > 1) {
					// 위
					int k = 1;
					while(i - k > 0) {
						if(board[i-k][j] == 0) k++;
						else if(board[i-k][j] == board[i][j]) break;
						else {
							if(k >= 3) {
								pq.add(new Road(board[i][j], board[i-k][j], k-1));
							}
							break;
						}
					}
					// 아래
					k = 1;
					while(i + k < N+1) {
						if(board[i+k][j] == 0) k++;
						else if(board[i+k][j] == board[i][j]) break;
						else {
							if(k >= 3) {
								pq.add(new Road(board[i][j], board[i+k][j], k-1));
							}
							break;
						}
					}
					// 오른
					k = 1;
					while(j + k < M+1) {
						if(board[i][j+k] == 0) k++;
						else if(board[i][j+k] == board[i][j]) break;
						else {
							if(k >= 3) {
								pq.add(new Road(board[i][j], board[i][j+k], k-1));
							}
							break;
						}
					}
					// 왼
					k = 1;
					while(j - k > 0) {
						if(board[i][j-k] == 0) k++;
						else if(board[i][j-k] == board[i][j]) break;
						else {
							if(k >= 3) {
								pq.add(new Road(board[i][j], board[i][j-k], k-1));
							}
							break;
						}
					}


				}
			}
		}

		// priorityQueue로 확인
		int answer = 0;
		island = new int[islandCnt+1];
		while(!pq.isEmpty()) {
			Road road = pq.poll();
			// 둘다 연결이 안되어있는 경우
			if(island[road.land1] == 0 && island[road.land2] == 0) {
				island[road.land1] = island[road.land2] = road.land1;
				answer += road.dis;
			}
			// 섬1은 연결되어있지않고, 섬2만 연결되어있는경우
			else if(island[road.land1] == 0 && island[road.land2] != 0) {
				island[road.land1] = island[road.land2];
				answer+= road.dis;
			}
			// 섬2은 연결되어있지않고, 섬1만 연결되어있는경우
			else if(island[road.land1] != 0 && island[road.land2] == 0) {
				island[road.land2] = island[road.land1];
				answer+= road.dis;
			}
			// 두 연결되어있는 섬을 연결해야하는 경우
			else if(island[road.land1] != island[road.land2]) {
				int checkVal = island[road.land2];
				for(int i=2; i<=islandCnt; i++) {
					if(island[i] == checkVal) island[i] = island[road.land1];
				}
				answer += road.dis;
			}
			// 두 섬이 이미 연결되어 있는경우는 패스
			if(checkIsland()) break;
		}

		if(!checkIsland()) System.out.println("-1");
		else System.out.println(answer);
	}
}

회고

  • 만약에 섬의 개수가 매우 많다면 Union & Find를 통해 섬의 연결을 관리하면 좋을 것 같다.
  • 약간 노가다 식으로 for문을 만들어서 코드가 너무 길었다. 다음번에 비슷한 문제를 풀면 도로를 찾을 때 재귀로 찾아 코드를 좀더 깔끔하게 만들어야겠다.

Written by@Gomster
항상 노력하고 나아가는 백엔드 개발자가 되고 싶은 대학생입니다.

GitHub