ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준] 1520번 내리막 길
    Algorithm Study/Python 2021. 5. 28. 02:06

    https://www.acmicpc.net/problem/1520

     

    1520번: 내리막 길

    여행을 떠난 세준이는 지도를 하나 구하였다. 이 지도는 아래 그림과 같이 직사각형 모양이며 여러 칸으로 나뉘어져 있다. 한 칸은 한 지점을 나타내는데 각 칸에는 그 지점의 높이가 쓰여 있으

    www.acmicpc.net

     

    문제

    여행을 떠난 세준이는 지도를 하나 구하였다. 이 지도는 아래 그림과 같이 직사각형 모양이며 여러 칸으로 나뉘어져 있다. 한 칸은 한 지점을 나타내는데 각 칸에는 그 지점의 높이가 쓰여 있으며, 각 지점 사이의 이동은 지도에서 상하좌우 이웃한 곳끼리만 가능하다.

    현재 제일 왼쪽 위 칸이 나타내는 지점에 있는 세준이는 제일 오른쪽 아래 칸이 나타내는 지점으로 가려고 한다. 그런데 가능한 힘을 적게 들이고 싶어 항상 높이가 더 낮은 지점으로만 이동하여 목표 지점까지 가고자 한다. 위와 같은 지도에서는 다음과 같은 세 가지 경로가 가능하다.

    지도가 주어질 때 이와 같이 제일 왼쪽 위 지점에서 출발하여 제일 오른쪽 아래 지점까지 항상 내리막길로만 이동하는 경로의 개수를 구하는 프로그램을 작성하시오.

     

    입력

    첫째 줄에는 지도의 세로의 크기 M과 가로의 크기 N이 빈칸을 사이에 두고 주어진다. 이어 다음 M개 줄에 걸쳐 한 줄에 N개씩 위에서부터 차례로 각 지점의 높이가 빈 칸을 사이에 두고 주어진다. M과 N은 각각 500이하의 자연수이고, 각 지점의 높이는 10000이하의 자연수이다.

     

    출력

    첫째 줄에 이동 가능한 경로의 수 H를 출력한다. 모든 입력에 대하여 H는 10억 이하의 음이 아닌 정수이다.

     

     

    풀이

    가능한 모든 경우를 찾는 물웅덩이 길찾기 문제와 비슷하기 때문에 DP를 이용해서 구현해야할 것 같았다.
    하지만 물웅덩이 문제와 같은 경우에는 최단거리만 구하기 때문에 되돌아갈 수 없지만 이 문제는 높이가 낮은 곳으로 이동하는 조건은 있지만 최단거리의 조건은 없기 때문에 4방향 모두로 이동하는 경우를 계산해야한다.
    즉, DP + DFS를 이용하여 구현하였다.

    dp = [[-1 for _ in range(COL)]for _ in range(ROW)]
    dp[ROW-1][COL-1] = 1
    def dfs(row, col):
    
        #방문한 적 있으면 값을 준다.
        if dp[row][col] != -1:
            return dp[row][col]
    
        #방문한 적이 없는 경우에는 4방향에서 올 수 있는 경우를 모두 더해서 출력
        temp = 0
        for dy, dx in direction:
            next_row, next_col = row + dy, col + dx
            if 0 <= next_row < ROW and 0 <= next_col < COL and board[row][col] > board[next_row][next_col]:
                temp += dfs(next_row, next_col)
        dp[row][col] = temp
        return dp[row][col]

    dp에는 갈 수 있는 경우의 수를 저장한다. 초기에 -1로 초기화 하여 방문한 적 있는 경우에는 해당 값을 리턴한다.
    방문한 적 없는 경우에는 4방향에 대하여 지금 내가 있는 위치로 올 수 있는지 역으로 계산하였다.
    board의 다음 좌표가 내 위치보다 낮은 경우에 올 수 있기 때문에 해당 좌표의 dp값들을 더해서 현재 위치의 dp값으로 구성하였다.

    dfs함수를 작동시키면 처음에 dp의 모든 값이 -1이기 때문에 마지막 좌표인 N-1, M-1까지 재귀가 돌아간 뒤 0, 0으로 돌아오는 방법으로 계산되는 것이다.

    결과에 RecursionError가 나왔다. 파이썬의 경우 재귀를 1000번인가까지 돌게 기본적으로 설정되어 있기 때문에 그 횟수가 넘어 에러가 난 것이다.

    sys.setrecursionlimit(10 ** 8)

    recursionlimit를 더 크게 변경하여 제출하니 문제없이 통과하였다.

     

     

    전체 코드

    import sys
    sys.setrecursionlimit(10 ** 8)
    
    ROW, COL = map(int, sys.stdin.readline().split())
    board = [list(map(int, sys.stdin.readline().split())) for _ in range(ROW)]
    dp = [[-1 for _ in range(COL)]for _ in range(ROW)]
    dp[ROW-1][COL-1] = 1
    
    direction = [[1, 0], [-1, 0], [0, 1], [0, -1]]
    
    def dfs(row, col):
        #방문한 적 있으면 값을 준다.
        if dp[row][col] != -1:
            return dp[row][col]
    
        #방문한 적이 없는 경우에는 4방향에서 올 수 있는 경우를 모두 더해서 출력
        temp = 0
        for dy, dx in direction:
            next_row, next_col = row + dy, col + dx
            if 0 <= next_row < ROW and 0 <= next_col < COL and board[row][col] > board[next_row][next_col]:
                temp += dfs(next_row, next_col)
        dp[row][col] = temp
    
        return dp[row][col]
    
    print(dfs(0, 0))
    

    dp + dfs 문제에 N, M좌표에서 0, 0으로 거슬러 올라오면서 풀기 때문에 다른 분들의 풀이를 보면서 풀었는데도 이해가 잘 되지 않아서 정말 하나하나 체크하면서 풀었다.

    'Algorithm Study > Python' 카테고리의 다른 글

    [백준] 12764번 싸지방에 간 준하  (0) 2021.05.29
    [백준] 21737번 SMUPC계산기  (0) 2021.05.28
    [백준] 1092번 배  (0) 2021.05.28
    [백준] 1254번 팰린드롬 만들기  (0) 2021.05.28
    [백준] 1213번 팰린드롬 만들기  (0) 2021.05.28

    댓글

From BlackHair