BOJ_17472_다리 만들기2

2021. 9. 24. 18:57백준 알고리즘(BOJ)

1

구분: 구현, 그래프 이론, 그래프 탐색, 브루트포스 알고리즘, 너비 우선 탐색, 깊이 우선 탐색, 최소 스패님 트리

언어: Java

전략

1. 각각 섬들을 구분하기

BFS를 통해 각 섬들을 구분한다. 섬들에 숫자를 부여한다. 

 

2. 각 섬들 간의 최소 거리 구하기 

각 섬들을 하나의 정점으로 여기기 위해 각 섬들의 최소 거리를 구한다. 

각 섬들에 대해 거리를 구하는데, 한 섬에 해당하는 모든 좌표를 모두 확인한다.  그래서 최소 거리를 구한다

구한 거리는 인접 행렬에 넣어 그래프 처럼 만든다. 

 

3. MST - 최소 스패닝 트리 구하기

prim 알고리즘과 kruskal 알고리즘이 있는데 나는 prim 알고리즘을 사용했다.

 

 


2. 코드

package com.ssafy.webex;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;


public class Main {

	static int w, h;
	static int[][]map, matrix;
	static boolean[][] check;
	static class Node{
		int y, x;
		Node(int y, int x){
			this.y = y;
			this.x = x;
		}
	}

	static Queue<Node> q;
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		
		h = scanner.nextInt();
		w = scanner.nextInt();
		map = new int[h][w];
		check = new boolean[h][w];
		
		for(int i = 0; i<h; i++) {
			for(int j = 0; j<w; j++) {
				map[i][j] = scanner.nextInt();
			}
		}
		
		q = new LinkedList<>();
		int num = 1;//각 섬에 부여할 번호
		for(int i = 0; i<h; i++) {
			for(int j = 0; j<w; j++) {
				if(!check[i][j] && map[i][j] == 1) {
					map[i][j] = num;
					check[i][j] = true;
					q.offer(new Node(i,j));
					setnum(num++);
				}
			}
		}

		matrix = new int[num][num];//그래프 형태로 하기위한, 노드와 그 가중치를 담은 인접 향렬
		for(int i = 1; i<num; i++) {
			for(int j = 1; j<num; j++) {
				if(i==j)
					matrix[i][j] = 0;
				else
					matrix[i][j] = Integer.MAX_VALUE;
			}
		}
		
		
		//각 섬들을 노드로 봐서 이젠 그 점간의 거리를 구해보자 
		for(int i = 0; i<h; i++) {
			for(int j = 0; j<w; j++) {
				if(map[i][j]!=0) {
					getlength(i, j);
				}
			}
		}
		
		//전체 섬의 개수는 num-1개 이다. 
		maketree(num-1);
		System.out.println(result);

	}//main
	
	//오른, 왼, 아래, 위
	static int[] dx = {1,-1,0,0};
	static int[] dy = {0,0,1,-1};
	
	//지도의 섬에 번호를 붙여주는 것 
	private static void setnum(int number) {
		while(!q.isEmpty()) {
			Node node = q.poll();
			int qy = node.y;
			int qx = node.x;
			
			for(int d = 0; d<4; d++) {
				int nx = qx + dx[d];
				int ny = qy + dy[d];
				
				if(nx<0||ny<0||nx>=w||ny>=h)continue; //범위체크
				if(map[ny][nx]==0) continue;//땅이 아니라면
				if(check[ny][nx]) continue;//방문여부 확인
				
				q.offer(new Node(ny, nx));
				map[ny][nx] = number;
				check[ny][nx] = true;
				
			}
			
		}
	}//setnum
	
	//static int index;
	//각 섬 별 거리를 구하는 함수 
	private static void getlength(int y, int x) {
		
		int num = map[y][x];
		
		//4방향 탐색 쭉쭉
		for(int d = 0; d<4; d++) {
			int nx = x;
			int ny = y;
			int length = 0;//다리 길이
			int dest = 0;//목적지 
			
			while(true) {		
				nx+=dx[d];
				ny+=dy[d];
				//범위 확인
				if(nx<0||ny<0||nx>=w||ny>=h) {
					length = 0;
					break;
				}				
				//나 자신이면 이제 그만
				if(map[ny][nx]==num) {
					length = 0;
					break;
				}
				//0이면 다리를 놓을 수 있다.
				if(map[ny][nx]==0) {
					length++;
					continue;
				}
				//이건 다른 섬에 도착한 것이다. 난 이미 걸러지니까
				if(map[ny][nx]!=0) {
					dest = map[ny][nx];
					break;
				}
			}//while
			
			//다리길이가 2 미만이면 지나가 
			if(length<2)
				continue;
			
			//더 최소값으로 
			matrix[num][dest] = Math.min(length, matrix[num][dest]);
			matrix[dest][num] = matrix[num][dest];
			
		}//4방향 탐색
	}//getlength;
	
	
	//이제 최소 신장 트리를 구하는것이 필요하다.
	static int result;
	static int[] minedge;
	//num은 전체 섬 개수 
	//prim알고리즘을 이용
	private static void maketree(int num) {
		
		minedge = new int[num+1];
		Arrays.fill(minedge, Integer.MAX_VALUE);
		//시작점은 0으로 
		minedge[1] = 0;
		result = 0;
		
		boolean[] visited = new boolean[num+1];
		
		for(int i = 1; i<=num; i++) {
			
			//1. 신장트리에 포함되지 않은 정점 중 최소 간선 비용 정점 찾기
			int min = Integer.MAX_VALUE;
			int minVertex = -1; //최소간선비용의 정점 번호
			for(int j = 1; j<=num; j++) {
				if(!visited[j] && min>minedge[j]) {
					min = minedge[j];
					minVertex = j;
				}
			}
			
			if(minVertex == -1) {
				result = -1;
				return;
			}
			visited[minVertex] = true; //신장트리에 포함시킴
			result+=min;  //간선비용 누적
			
			//2. 선택된 정점 기준으로 신장트리에 연결되지 않은 타 정점과의 간선비용 최소로 업데이트 
			for(int j = 1; j<=num; j++) {
				if(!visited[j] &&  matrix[minVertex][j]!=0 && minedge[j] > matrix[minVertex][j]) {
						minedge[j] = matrix[minVertex][j];
				}
			}
			
		}
		
		
	}//mst
	
}

 


3

  • 최소 스패닝 트리를 만드는 알고리즘이 낯설다. 공부가 많이 필요하다. 
  • BFS는 Queue에 넣기 전에 방문 체크나 이런 저런 것들을 다 하고, Queue에 넣어야 한다. - 이것 때문에 모두 앞에서 조금 부끄러웠다. 이런 실수를 계속한다. 고치자 꼭!
  • 섬을 구성하는 모든 점들을 통해 거리를 구해야 하는 점을 생각해내기 어려웠다. 

 

'백준 알고리즘(BOJ)' 카테고리의 다른 글

BOJ_10972_다음 순열  (0) 2021.09.28
BOJ_12865_평범한 배낭  (0) 2021.09.27
BOJ_17471_게리맨더링  (0) 2021.09.24
BOJ_1697_숨바꼭질  (0) 2021.09.22
BOJ_1600_ 말이 되고픈 원숭이  (0) 2021.09.17