문제주소 :programmers.co.kr/learn/courses/30/lessons/1832
코딩테스트 연습 - 보행자 천국
3 3 [[0, 0, 0], [0, 0, 0], [0, 0, 0]] 6 3 6 [[0, 2, 0, 0, 0, 2], [0, 0, 2, 0, 1, 0], [1, 0, 0, 2, 2, 0]] 2
programmers.co.kr
<문제 설명>
문제 설명
보행자 천국
카카오내비 개발자인 제이지는 시내 중심가의 경로 탐색 알고리즘 개발 업무를 담당하고 있다. 최근 들어 보행자가 자유롭고 편리하게 걸을 수 있도록 보행자 중심의 교통 체계가 도입되면서 도심의 일부 구역은 자동차 통행이 금지되고, 일부 교차로에서는 보행자 안전을 위해 좌회전이나 우회전이 금지되기도 했다. 복잡해진 도로 환경으로 인해 기존의 경로 탐색 알고리즘을 보완해야 할 필요가 생겼다.
도시 중심가의 지도는 m × n 크기의 격자 모양 배열 city_map으로 주어진다. 자동차는 오른쪽 또는 아래 방향으로 한 칸씩 이동 가능하다.
city_map[i][j]에는 도로의 상황을 나타내는 값이 저장되어 있다.
- 0인 경우에는 자동차가 자유롭게 지나갈 수 있다.
- 1인 경우에는 자동차 통행이 금지되어 지나갈 수 없다.
- 2인 경우는 보행자 안전을 위해 좌회전이나 우회전이 금지된다. (왼쪽에서 오던 차는 오른쪽으로만, 위에서 오던 차는 아래쪽으로만 진행 가능하다)

도시의 도로 상태가 입력으로 주어졌을 때, 왼쪽 위의 출발점에서 오른쪽 아래 도착점까지 자동차로 이동 가능한 전체 가능한 경로 수를 출력하는 프로그램을 작성하라. 이때 가능한 경로의 수는 컴퓨터가 표현할 수 있는 정수의 범위를 넘어설 수 있으므로, 가능한 경로 수를 20170805로 나눈 나머지 값을 출력하라.
입력 형식
입력은 도시의 크기를 나타내는 m과 n, 그리고 지도를 나타내는 2차원 배열 city_map으로 주어진다. 제한조건은 아래와 같다.
- 1 <= m, n <= 500
- city_map의 크기는 m × n이다.
- 배열의 모든 원소의 값은 0, 1, 2 중 하나이다.
- 출발점의 좌표는 (0, 0), 도착점의 좌표는 (m - 1, n - 1)이다.
- 출발점과 도착점의 city_map[i][j] 값은 0이다.
출력 형식
출발점에서 도착점까지 이동 가능한 전체 경로의 수를 20170805로 나눈 나머지를 리턴한다.
예제 입출력
m n city_map answer3 | 3 | [[0, 0, 0], [0, 0, 0], [0, 0, 0]] | 6 |
3 | 6 | [[0, 2, 0, 0, 0, 2], [0, 0, 2, 0, 1, 0], [1, 0, 0, 2, 2, 0]] | 2 |
예제에 대한 설명
첫 번째 예제는 모든 도로가 제한 없이 통행 가능한 경우로, 가능한 경우의 수는 6가지이다.
두 번째 예제는 문제 설명에 있는 그림의 경우이다. 가능한 경우의 수는 빨간 실선과 노란 점선 2가지뿐이다.
<풀이법>
▒ 한줄 개념: dp ▒
모든 경로 수를 체크하는 문제로, 동적 계획법을 사용해야 하는 문제이다.
기본적으로 (0, 0) 에서 (m-1, n-1) 위치로 이동해야하는 것으로, 경로는 왼쪽 위에서 오른쪽 아래로 내려간다.
보통 이런 문제에서는 dp를 2차원 배열로 구성하고, 바로 윗 칸과 바로 왼쪽 칸의 경로 값을 더해주면 되는데, 여기에서는 cityMap의 값이 3개 종류이다. 따라서 dp도 3가지 경우에 따라 구성하면 된다.
<기본>
dp[i][j] = dp[i-1][j] + dp[i][j-1]
=> 왼쪽에서 오는 경로 + 위에서 오는 경로 = 현재 칸의 경로
<cityMap의 3가지 경우>
1. cityMap[i-1][j] = 0, cityMap[i][j-1] = 0
=> 윗 칸, 왼쪽칸 모두 이상 없음. 기본과 같다.
=> (왼쪽으로 오는 경로 + 위쪽으로 오는 경로)
2. cityMap[i-1][j] = 1 or cityMap[i][j-1] = 1
=> 둘 중에 하나라도 통행 금지된 길이면, 해당 경로에서 오는 값은 더하지 않는다. 즉, 0을 더하는 것과 같다.
=> 왼쪽이 통행금지, 윗쪽은 정상: 위로 오는 경로만 더해줌. ( 0 + 위로오는 경로 )
=> 위쪽이 통행금지, 왼쪽은 정상: 왼쪽으로 오는 경로만 더해줌. (왼쪽으로 오는 경로 + 0)
=> 둘다 통행금지의 경우 ( 0 + 0 )
3. cityMap[i-1][j] = 2 or cityMap[i][j-1] = 2
=> 둘 중에 하나라도 좌회전이나 우회전이 금지될 경우
=> 위쪽으로 오는 경로는 그 위치의 위쪽으로 오는 경로만 가져올 수 있다.
=> 왼쪽으로 오는 경로는 그 위치의 왼쪽에서 오는 경로만 가져올 수 있다.
=> 즉, 모든 위치에 대해서 위쪽으로 오는 경로, 왼쪽으로 오는 경로를 구분해주어야 한다.
=> 따라서 3차원 배열이 필요( dp[][][] = new int[m][n][2] )
<코드(Java)>
class Solution {
int[][][] dp;
int MOD = 20170805;
public int solution(int m, int n, int[][] cityMap) {
int answer = 0;
// 내려오는 거 0, 왼쪽에서 오는거 1
this.dp = new int[m][n][2];
if(cityMap[0][0] == 1) return 0;
dp[0][0][0] = 1;
dp[0][0][1] = 1;
for(int i = 1; i < m; i++){
if(cityMap[i][0] != 1) dp[i][0][0] = dp[i-1][0][0];
}
for(int i = 1; i < n; i++){
if(cityMap[0][i] != 1) dp[0][i][1] = dp[0][i-1][1];
}
for(int i = 1; i < m; i++){
for(int j = 1; j < n; j++){
if(cityMap[i-1][j] != 1){
if(cityMap[i-1][j] == 2) dp[i][j][0] = dp[i-1][j][0];
else dp[i][j][0] = (dp[i-1][j][0] + dp[i-1][j][1]) % MOD;
}
if(cityMap[i][j-1] != 1){
if(cityMap[i][j-1] == 2) dp[i][j][1] = dp[i][j-1][1];
else dp[i][j][1] = (dp[i][j-1][0] + dp[i][j-1][1]) % MOD;
}
}
}
return (dp[m-1][n-1][0] + dp[m-1][n-1][1]) % MOD;
}
}
더 많은 코드 보기(GitHub) : github.com/dwkim-97/CodingTest
'Programmers' 카테고리의 다른 글
[프로그래머스] 블록 게임 / Java (0) | 2021.03.31 |
---|---|
[프로그래머스] 무지의 먹방 라이브 / Java (3) | 2021.03.31 |
[프로그래머스] 스티커 모으기(2) / Java (0) | 2021.03.24 |
[프로그래머스] 카드 짝 맞추기 / Java (2) | 2021.03.24 |
[프로그래머스] 매칭 점수 / Java (0) | 2021.03.24 |