계속되는 폭우로 일부 지역이 물에 잠겼습니다. 물에 잠기지 않은 지역을 통해 학교를 가려고 합니다. 집에서 학교까지 가는 길은 m x n 크기의 격자모양으로 나타낼 수 있습니다.
가장 왼쪽 위, 즉 집이 있는 곳의 좌표는 (1, 1)로 나타내고 가장 오른쪽 아래, 즉 학교가 있는 곳의 좌표는 (m, n)으로 나타냅니다.
격자의 크기 m, n과 물이 잠긴 지역의 좌표를 담은 2차원 배열 puddles이 매개변수로 주어집니다. 오른쪽과 아래쪽으로만 움직여 집에서 학교까지 갈 수 있는 최단경로의 개수를 1,000,000,007로 나눈 나머지를 return 하도록 solution 함수를 작성해주세요.
m | n | puddles | return |
---|---|---|---|
4 | 3 | [[2, 2]] | 4 |
해당 문제는 아쉽지만 프로그래머스에서 JS로 풀 수 없다. 하지만 JS로 아득바득 풀어보자!
해당 문제는 DP로 접근할 수 있다. 애초에 카테고리가 DP로 분류가 되어있기도 하고, 보통 DP 문제에서 연산의 결과가 엄청나게 커지는 경우가 종종 있어서, 문제 조건 중에 어떤 수를 나눈 나머지를 리턴하라 하는 경우면 DP를 적용할 수 있는 가능성이 높다.
해당 문제는 누적합(Prefix Sum)을 구하는 방식과 유사하게 풀 수 있다. DP 문제를 풀기위해서는 항상 점화식을 찾아야 하는데, 해당 문제의 점화식은 말로 설명하는 것 보다 아래 그림을 보는 것이 더 이해가 쉬울 것 이다.
목적지까지 이동할 수 있는 방향은 오른쪽과 아래밖에 없다고 주어져있다. 때문에 집에서부터 직선으로 이어지는 길은 모두 단 하나의 경로밖에 존재하지 않는다. 이 조건을 가지고 우리는 DP 초기화를 진행할 수 있다.
직선상에 있지 않는 경로는 항상 다음 식이 성립한다.
때문에 위 그림에서 1을 제외한 나머지 숫자들은 자신을 기준으로 왼쪽칸과 위쪽칸의 합이라는 것을 알 수 있다.
이때 만약 문제처럼 특정 경로에 물 웅덩이가 생겨서 그 길을 이용하지 못하는 상황이 생기면 어떻게 계산되어야 할까? 다시 아래 그림을 보자.
[2, 2] 위치에 물 웅덩이가 생겨 해당 칸을 이용할 수 없다. 이용을 하지 못한다는 것은 그 값을 0으로 취급하면 된다. 해당 칸을 0으로 두고 위에서 찾은 점화식을 그대로 적용하면 다음과 같이 최종 목적지까지 도달할 수 있는 경우의 수는 4라는 것을 확인할 수 있다.
따라서 주어진 puddles 위치에 해당하는 위치 좌표일 땐 해당 값을 0으로 간주하고, 그 외 계속 계산을 이어가면 된다.
바로 전체 코드를 확인하자.
코드
function solution(m, n, puddles) {
const dp = new Array(n+1).fill().map(_ => new Array(m+1).fill(1));
// [n+1][m+1] 크기의 배열을 0으로 초기화한다.
// 각각 +1을 해주는 이유는 역시 DP 에서 많이 쓰이는 테크닉인데
// 계산의 편의성을 위해 배열의 첫번째 값은 비워두고 사용하기 위해 선언하는 경우가 많다.
// m과 n의 위치에 주의하자! 문제에서는 m이 x축, n이 y축이다.
for(let i = 1; i < n; i++) {
for(let j = 1; j < m; j++) {
if(i === 1 && j === 1) {
// 집의 위치를 1로 설정해주면
// 아래 점화식을 통해 별다른 조건없이 직선 경로를 1로 초기화할 수 있다.
dp[1][1] = 1;
continue;
}
// 해당 반복문에서 j가 x좌표, i가 y좌표인 것에 주의하자!
if(isPuddle(j, i, puddles)) continue;
dp[i][j] = (dp[i-1][j] + dp[i][j-1]) % 1000000007;
}
}
return dp[n][m];
}
// 주어진 좌표에 물 웅덩이가 있다면 true를 반환한다
// 이때 x, y좌표의 순서에 주의하자!
const isPuddle = (x, y, puddles) => {
for(const puddle of puddles) {
if(puddle[0] === y && puddle[1] === x)
return true;
}
return false;
}
주어진 테스트 케이스로는 크롬 개발자 도구 콘솔에서 통과하는 것을 확인했다. 똑같은 알고리즘으로 짠 JAVA 코드가 통과했으므로 JS 역시 통과하지 않을까 싶다..