문제
N x N 크기인 정사각 격자 형태의 지형이 있습니다. 각 격자 칸은 1 x 1 크기이며, 숫자가 하나씩 적혀있습니다. 격자 칸에 적힌 숫자는 그 칸의 높이를 나타냅니다.
이 지형의 아무 칸에서나 출발해 모든 칸을 방문하는 탐험을 떠나려 합니다. 칸을 이동할 때는 상, 하, 좌, 우로 한 칸씩 이동할 수 있는데, 현재 칸과 이동하려는 칸의 높이 차가 height 이하여야 합니다. 높이 차가 height 보다 많이 나는 경우에는 사다리를 설치해서 이동할 수 있습니다. 이때, 사다리를 설치하는데 두 격자 칸의 높이차만큼 비용이 듭니다. 따라서, 최대한 적은 비용이 들도록 사다리를 설치해서 모든 칸으로 이동 가능하도록 해야 합니다. 설치할 수 있는 사다리 개수에 제한은 없으며, 설치한 사다리는 철거하지 않습니다.
각 격자칸의 높이가 담긴 2차원 배열 land와 이동 가능한 최대 높이차 height가 매개변수로 주어질 때, 모든 칸을 방문하기 위해 필요한 사다리 설치 비용의 최솟값을 return 하도록 solution 함수를 완성해주세요.
[제한사항]
- land는 N x N크기인 2차원 배열입니다.
- land의 최소 크기는 4 x 4, 최대 크기는 300 x 300입니다.
- land의 원소는 각 격자 칸의 높이를 나타냅니다.
- 격자 칸의 높이는 1 이상 10,000 이하인 자연수입니다.
- height는 1 이상 10,000 이하인 자연수입니다.
풀이
BFS, MST(최소신장트리) 알고리즘을 이용하여 푸는 문제다.
알고리즘 순서
- BFS를 이용하여 사다리를 이용하지 않고 갈 수 있는 구간의 번호를 붙인다.
- 구간이 다른 부분에서의 구간 번호와 차를 list에 저장한다.
- list 를 land 비용을 기준으로 오름차순 정렬한다.
- 크루스칼 알고리즘을 통해서 구간별 최소 비용을 구해서 더해준다. (Union-Find 사용)
위의 순서로 구해주면 된다.
코드의 필요한 설명은 주석으로 설명해 두었다.
import java.util.*;
class Solution {
static int[] dx = {0, 1, 0, -1};
static int[] dy = {1, 0, -1, 0};
static int[] parent;
static int[][] visited;
static int area;
static List<vertex> list;
static class point{
int x;
int y;
point(int x, int y){
this.x = x;
this.y = y;
}
}
static class vertex implements Comparable<vertex>{
int u;
int v;
int weight;
vertex(int u, int v, int weight){
this.u = u;
this.v = v;
this.weight = weight;
}
@Override
public int compareTo(vertex v){
return Integer.compare(this.weight, v.weight);
}
}
public int solution(int[][] land, int height) {
int answer = 0;
visited = new int[land.length][land.length];
for(int i = 0 ; i < land.length ; i++){
Arrays.fill(visited[i], -1);
}
area = 0;
//구간별 번호를 붙여준다.
for(int i = 0 ; i < land.length ; i++){
for(int j = 0 ; j < land.length ; j++){
if(visited[i][j] < 0){
BFS(land, height, area++, j, i);
}
}
}
//구간 차를 list에 넣어준다.
list = new ArrayList<>();
for(int i = 0 ; i < land.length ; i++){
for(int j = 0 ; j < land.length ; j++){
for(int k = 0 ; k < 4; k++){
int nx = j + dx[k];
int ny = i + dy[k];
if(nx < 0 || nx >= land.length || ny < 0 || ny >= land.length) { continue;}
if(visited[i][j] == visited[ny][nx]) { continue;}
list.add(new vertex(visited[i][j], visited[ny][nx], Math.abs(land[i][j] - land[ny][nx])));
}
}
}
parent = new int[area];
for(int i = 0 ; i < area ; i++){
parent[i] = i;
}
//리스트를 구간 비용을 기준으로 오름차순정렬한다.
Collections.sort(list);
//union-find를 이용하여 최소신장트리를 완성한다.
int count = 0;
for(vertex Vertex : list){
if(union(Vertex.u, Vertex.v)){
answer += Vertex.weight;
//모든 구간이 연결되면 반복문을 나가준다.
if(++count == area -1){ break; }
}
}
return answer;
}
static int find(int value){
if(parent[value] == value){
return value;
}
return parent[value] = find(parent[value]);
}
static boolean union(int a, int b){
a = find(a);
b = find(b);
if(a == b){
return false;
}
if(a > b){
parent[a] = b;
}
else{
parent[b] = a;
}
return true;
}
static void BFS(int[][] land, int height, int area, int x, int y){
visited[y][x] = area;
Queue<point> queue = new LinkedList<point>();
queue.add(new point(x, y));
while(!queue.isEmpty()){
point now = queue.poll();
int nowX = now.x;
int nowY = now.y;
for(int i = 0 ; i < 4; i++){
int nx = nowX + dx[i];
int ny = nowY + dy[i];
if(nx < 0 || nx >= land.length || ny < 0 || ny >= land.length){ continue; }
if(visited[ny][nx] >= 0) { continue; }
if(Math.abs(land[nowY][nowX] - land[ny][nx]) > height) { continue; }
visited[ny][nx] = area;
queue.add(new point(nx, ny));
}
}
}
}
'알고리즘 공부 > 최단거리 알고리즘' 카테고리의 다른 글
[백준] 1956 운동 - 플로이드 와샬 (0) | 2021.08.22 |
---|---|
[백준] 18352 특정 거리의 도시 찾기 - 다익스트라알고리즘 (0) | 2021.08.22 |
[프로그래머스] 게임 맵 최단거리 - *BFS, 다익스트라 (0) | 2021.05.27 |
[프로그래머스] 환승 택시 요금 (0) | 2021.05.25 |
[프로그래머스] 경주로 건설 (0) | 2021.05.07 |