[프로그래머스] LV.3 등굣길 (JS)

KG·2021년 4월 9일
0

알고리즘

목록 보기
6/61
post-thumbnail

문제

계속되는 폭우로 일부 지역이 물에 잠겼습니다. 물에 잠기지 않은 지역을 통해 학교를 가려고 합니다. 집에서 학교까지 가는 길은 m x n 크기의 격자모양으로 나타낼 수 있습니다.

가장 왼쪽 위, 즉 집이 있는 곳의 좌표는 (1, 1)로 나타내고 가장 오른쪽 아래, 즉 학교가 있는 곳의 좌표는 (m, n)으로 나타냅니다.

격자의 크기 m, n과 물이 잠긴 지역의 좌표를 담은 2차원 배열 puddles이 매개변수로 주어집니다. 오른쪽과 아래쪽으로만 움직여 집에서 학교까지 갈 수 있는 최단경로의 개수를 1,000,000,007로 나눈 나머지를 return 하도록 solution 함수를 작성해주세요.

제한

  • 격자의 크기 m, n은 1 이상 100 이하인 자연수입니다.
    • m과 n이 모두 1인 경우는 입력으로 주어지지 않습니다.
  • 물에 잠긴 지역은 0개 이상 10개 이하입니다.
  • 집과 학교가 물에 잠긴 경우는 입력으로 주어지지 않습니다.

입출력 예시

mnpuddlesreturn
43[[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 역시 통과하지 않을까 싶다..


출처

https://programmers.co.kr/learn/courses/30/lessons/42898

profile
개발잘하고싶다

0개의 댓글