프로그래머스 > 2020 카카오 인턴십 > 경주로 건설 (Level 3)
건설회사의 설계사인 죠르디
는 고객사로부터 자동차 경주로 건설에 필요한 견적을 의뢰받았습니다.
제공된 경주로 설계 도면에 따르면 경주로 부지는 N x N
크기의 정사각형 격자 형태이며 각 격자는 1 x 1
크기입니다.
설계 도면에는 각 격자의 칸은 0
또는 1
로 채워져 있으며, 0
은 칸이 비어 있음을 1
은 해당 칸이 벽으로 채워져 있음을 나타냅니다.
경주로의 출발점은 (0, 0) 칸(좌측 상단)이며, 도착점은 (N-1, N-1) 칸(우측 하단)입니다. 죠르디는 출발점인 (0, 0) 칸에서 출발한 자동차가 도착점인 (N-1, N-1) 칸까지 무사히 도달할 수 있게 중간에 끊기지 않도록 경주로를 건설해야 합니다.
경주로는 상, 하, 좌, 우로 인접한 두 빈 칸을 연결하여 건설할 수 있으며, 벽이 있는 칸에는 경주로를 건설할 수 없습니다.
이때, 인접한 두 빈 칸을 상하 또는 좌우로 연결한 경주로를 직선 도로
라고 합니다.
또한 두 직선 도로
가 서로 직각으로 만나는 지점을 코너
라고 부릅니다.
건설 비용을 계산해 보니 직선 도로
하나를 만들 때는 100원이 소요되며, 코너
를 하나 만들 때는 500원이 추가로 듭니다.
죠르디는 견적서 작성을 위해 경주로를 건설하는 데 필요한 최소 비용을 계산해야 합니다.
도면의 상태(0은 비어 있음, 1은 벽)을 나타내는 2차원 배열 board가 매개변수로 주어질 때, 경주로를 건설하는데 필요한 최소 비용을 return 하도록 solution 함수를 완성해주세요.
[제한사항]
private static int N;
private static int[] dr = {0, 1, 0, -1};
private static int[] dc = {1, 0, -1, 0};
private static class Car implements Comparable<Car> {
int r, c, dir, sum;
public Car(int r, int c, int dir, int sum) {
this.r = r;
this.c = c;
this.dir = dir;
this.sum = sum;
}
@Override
// 최소 비용부터 탐색
public int compareTo(Car o) {
return this.sum-o.sum;
}
}
static public int solution(int[][] board) {
N = board.length;
return build(0, 0, board);
}
private static int build(int i, int j, int[][] board) {
int answer = 0;
// 네 방향으로 방문체크
int[][][] minSum = new int[N][N][4];
// 출발점에서 출발
minSum[i][j][3] = minSum[i][j][2] = minSum[i][j][1] = minSum[i][j][0] = -1;
PriorityQueue<Car> queue = new PriorityQueue<Car>();
if (board[i][j+1] == 0) {
queue.add(new Car(i, j+1, 0, 100)); // 오른쪽
minSum[i][j+1][0] = 100;
}
if (board[i+1][j] == 0) {
queue.add(new Car(i+1, j, 1, 100)); // 아래
minSum[i+1][j][1] = 100;
}
while (!queue.isEmpty()) {
Car tmp = queue.poll();
int r = tmp.r;
int c = tmp.c;
int dir = tmp.dir;
int sum = tmp.sum;
// 도착점에 도착하면 종료
if (r == N-1 && c == N-1) {
answer = sum;
break;
}
// 도착하지 않았으면 계속 진행
for (int d=0; d<4; d++) {
int row = r+dr[d];
int col = c+dc[d];
int s = sum + (d == dir ? 100 : 600); // 코너 필요한지 체크
if (row<0 || row>=N || col<0 || col>=N || board[row][col] == 1) continue;
// 현재 지점에 대한 비용이 더 작을 때만 계속 진행
if (minSum[row][col][d] != 0 && minSum[row][col][d] <= s) continue;
queue.add(new Car(row, col, d, s));
minSum[row][col][d] = s;
}
}
return answer;
}
BFS는 역시 방문 체크가 중요하다ㅠ 알면서 맨날 삽질... 문제를 읽고 BFS라는 접근 방법을 떠올리기는 쉽지만, 도착점까지 그냥 가기만 하면 되는 최단 거리가 아니라 직선과 코너의 비용이 다를 때 도착점까지의 최소 비용을 구해야 하므로 추가적인 작업이 필요해서 시간을 꽤 소요했다. 다음엔 여기서 시간 낭비하지 말자!!! 아무튼 어떤 상황(여기서는 방향)으로 해당 지점에 도착 하는 지에 따라서 추후의 비용에 영향이 있는 경우에는 꼭 상황별로 방문 체크(비용 체크) 해줍시다~~ 명심 또 명심!✊
풀고 나니까 비슷한 문제로 [백준] 2206. 벽 부수고 이동하기 랑 [백준] 17836. 공주님을 구해라! 가 떠올랐다. 많이 비슷한 건 아니지만 둘 다 BFS를 이용하고 방문 체크에 신경을 써야하는 문제라 개인적으로 느낌이 비슷...🤔 근데 그때도 헤매고 또 헤맸네...ㅎ 다음엔 바로 풀자!