[알고리즘 - DP] 프로그래머스 - 등굣길

2023. 3. 16. 00:25·개발 공부/알고리즘

문제

문제 링크

풀이

문제 이해

아래와 같은 m x n 크기의 격자가 있을 때,

image-20230316002515527

가장 왼쪽 위 (1, 1) 에서 가장 우측 아래쪽 (m, n) 으로 가는 방법의 개수를 구하는 문제이다. (1,000,000,007로 나눈 나머지 반환) 단, 이때 위 그림에서처럼 웅덩이가 있는 곳은 지나가지 못한다.

입력으로는 격자의 크기 m, n과 웅덩이의 좌표가 [[2, 2]]와 같은 형태로 puddles 이름으로 제공된다.

풀이 방법

문제에서 우측 및 아래로 움직이는 경로만 가능하다고 했으므로 (1, 1)에서 시작하여 (m, n)까지 우측 및 아래로 한 칸씩 이동하며 경로의 개수를 계산한다. 이때, 예를 들어 (3, 3)까지의 경로 경우의 수는 아래와 같이 (2, 3) 까지의 경로 경우의 수와 (3, 2) 까지의 경로 경우의 수를 더한 값과 같다.

image-20230315235440998

이런식으로 계속 반복하다보면 (m, n) 위치의 경우의 수도 아래와 같이 구할 수 있다.

image-20230315235543413

따라서 점화식을 세워보면, answer[m, n] = answer[m - 1][n] + answer[m][n-1] 이라 할 수 있다.

이제 위 점화식을 사용해 코드를 작성하면 되는데 아래 두 가지를 고려해야 한다.

  1. 웅덩이에 접근하는 경로(puddles)는 사용하지 않을 것
  2. 한 번 구한 경로는 다시 계산하지 않도록 메모이제이션을 적용할 것

위 점들을 고려해 재귀로 작성한 코드는 아래와 같다.

class Solution {
    public int solution(int m, int n, int[][] puddles) {
        int[][] answer = new int[m + 1][n + 1];        

        // 물에 잠긴 지역 별도 배열로 생성
        int[][] processedPuddles = new int[m + 1][n + 1];
        for (int i = 0 ; i < puddles.length ; i++) {
            processedPuddles[puddles[i][0]][puddles[i][1]] = -1;
        }

        // 재귀 호출
        return search(m, n, m, n, answer, processedPuddles);
    }

    public int search(int m, int n,
                       int x, int y,
                       int[][] answer, int[][] processedPuddles) {
        // 초기 케이스
        if (x == 1 && y == 1) {
            return 1;
        }
        // 범위를 벗어나면 제외
        if (x > m || x < 1 || y > n || y < 1) {
            return 0;
        }
        // 물에 잠긴 지역은 제외
        if (processedPuddles[x][y] == -1) 
            return 0;

        // 메모이제이션
        if (answer[x][y] != 0) {
            return answer[x][y];
        }

        // 나머지 정상 케이스
        return answer[x][y] = (search(m, n, x - 1, y, answer, processedPuddles) 
                               + search(m, n, x, y - 1, answer, processedPuddles)) 
                                % 1_000_000_007;
    }
}

근데 뭔가 짜고 보니 재귀보다 반복이 더 보기 좋을 것 같아서 다시 작성했다. ㅎㅎ

class Solution {
    public int solution(int m, int n, int[][] puddles) {
        int[][] answer = new int[m + 1][n + 1];

        // 물에 잠긴 지역 처리
        for (int i = 0 ; i < puddles.length ; i++) {
            answer[puddles[i][0]][puddles[i][1]] = -1;
        }

        // 초기 좌표 값 설정
        answer[1][1] = 1;

        // 순회하며 경우의 수 계산
        for (int x = 1 ; x <= m ; x++) {
            for (int y = 1 ; y <= n ; y++) {
                // 물에 잠긴 지역 제외
                if (answer[x][y] == -1) {
                    answer[x][y] = 0;
                    continue;
                }

                // 나머지 정상 경로 계산
                if (x != 1) {
                    answer[x][y] += answer[x - 1][y] % 1_000_000_007;
                }
                if (y != 1) {
                    answer[x][y] += answer[x][y - 1] % 1_000_000_007;
                }
            }
        }
        return answer[m][n] % 1_000_000_007;
    }
}

코드에서 주의해야 할 부분들은 아래와 같다.

  1. 계산을 모두 마치고 반환할 때만 1,000,000,007로 나눈 나머지를 반환하는 것과 매번 나누고 더해주는 것의 값 차이는 없으나 후자의 방법을 사용해야 효율성 테스트에서 탈락하지 않는다.
  2. 물에 잠긴 지역을 처리할 때 순회 방식으로 하게 되면 물에 잠긴 지역을 단 한 번만 방문하기 때문에 굳이 별도의 배열을 사용하지 않고 answer 배열에 -1 등으로 저장해두고 해당 좌표를 지나갈 때 0으로 값을 바꿔주면서 continue 처리해주면 해당 좌표를 경로로 사용하지 않도록 처리가 가능하다.
  3. 초기 좌표 값에 위치하는 경우의 수는 한 가지이므로 answer[1][1]은 1로 초기화해준다.
  4. 순회로 풀이하는 경우 x가 1가 아닐 때 좌측 좌표의 경우의 수를 현재 좌표에 더해주고, y가 1이 아닐 때 위쪽 좌표의 경우의 수를 현재 좌표에 더해준다.

'개발 공부 > 알고리즘' 카테고리의 다른 글

[알고리즘 - 완전탐색] 전력망을 둘로 나누기  (0) 2023.04.13
[알고리즘] 전화번호 목록  (0) 2023.04.06
[알고리즘 - 그래프] 다익스트라 - 가장 먼 노드  (0) 2023.03.30
[알고리즘 - graph] 네트워크  (0) 2023.03.23
[알고리즘] graph와 탐색  (0) 2023.03.21
'개발 공부/알고리즘' 카테고리의 다른 글
  • [알고리즘] 전화번호 목록
  • [알고리즘 - 그래프] 다익스트라 - 가장 먼 노드
  • [알고리즘 - graph] 네트워크
  • [알고리즘] graph와 탐색
gmelon
gmelon
백엔드 개발을 공부하고 있습니다.
  • gmelon
    gmelon's greenhouse
    gmelon
  • 전체
    오늘
    어제
    • 분류 전체보기 (91)
      • 개발 공부 (28)
        • Java (6)
        • Spring (10)
        • 알고리즘 (11)
        • 기타 (1)
      • 프로젝트 (12)
        • [앱] 플랭고 (4)
        • 졸업 프로젝트 (8)
      • 스터디 (0)
        • 자바 (30)
      • 기록 (15)
        • 후기, 회고 (9)
        • SSAFYcial (5)
        • 이것저것 (1)
      • etc. (6)
        • 모각코 (6)
  • 블로그 메뉴

    • 홈
    • 방명록
    • github
    • 스크랩
  • 인기 글

  • 태그

    groupingBy mapping
    태초마을이야
    졸업프로젝트
    자바
    싸피 회고
    프리티어 종료
    Java Collector
    AWS 프리티어 종료
    groupingBy()
    Collector groupingBy()
    2024 상반기 회고
    2024 회고
    java
    한글프로그래밍언어
    자바 Collector
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
gmelon
[알고리즘 - DP] 프로그래머스 - 등굣길
상단으로

티스토리툴바