본문 바로가기

알고리즘/백준

[백준] 1520. 내리막길 Python

문제 링크 : 1520 내리막길

 

1520번: 내리막 길

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

www.acmicpc.net

 

풀이 코드 : GIthub
 

문제

 

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

 

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

 

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

 

문제

풀이 

처음에는 간단한 DFS문제로 생각하였다. 다음과 같은 방법으로 풀이를 생각하였다.

  • DFS로 0,0 부터 주변의 높이가 낮은 지점을 방문한다.
  • 방문한 지점은 방문하였다고 표기한다.
  • DFS로 목표지점에 방문하면 count를 1 더한다
  • 모든 DFS를 수행하면 count를 출력한다.

 

코드

import sys
from collections import deque

n, m = map(int, sys.stdin.readline().split(' '))

maps = []

for i in range(n):    
	maps.append(list(map(int, input().split())))


visited = [[0] * m for _ in range(n)]
visited[0][0] = 1


dx = [1, -1, 0, 0]
dy = [0, 0, 1, -1]

count = 0


def dfs(x, y):
    global count
    if x == n-1 and y == m-1:
        visited[x][y] = 0
        count += 1
        return
    for direction in range(4):
        new_dx = x + dx[direction]
        new_dy = y + dy[direction]
        if 0 <= new_dx < n and 0 <= new_dy < m:
            if maps[new_dx][new_dy] == 0 or visited[new_dx][new_dy] == 1:
                continue
            else:
                if (maps[new_dx][new_dy]) < (maps[x][y]) and visited[new_dx][new_dy] == 0:
                    visited[new_dx][new_dy] = 1
                    dfs(new_dx, new_dy)
        visited[x][y]  = 0




dfs(0, 0)

print(count)

 

 

결과

 

시간초과가 나왔다.

 

문제점은 다음과 같았다.

 

문제에서 목적지 까지 가는 방법은 아래와 같이 3가지가 있다. 

여기서 2번째 3번째 [20 -> 17 -> 15 -> 10]부분이 겹친다.

그렇다면 3번째 경로에서 20 부분에 들어갔을 때 더이상 진행할 필요가 없고(어차피 목적지에 도달할 수 있으므로) 바로 count에 추가하면 된다.

 

두번째 풀이

https://fre2-dom.tistory.com/251

 

[baekjoon] 백준 1520번(파이썬): 내리막 길

문제 1520번: 내리막 길 여행을 떠난 세준이는 지도를 하나 구하였다. 이 지도는 아래 그림과 같이 직사각형 모양이며 여러 칸으로 나뉘어져 있다. 한 칸은 한 지점을 나타내는데 각 칸에는 그 지

fre2-dom.tistory.com

위 블로그 글을 참고하여 풀었다.

DP를 활용하여 중복된 연산을 피하는 것이 핵심이였다.

 

코드

import sys
from collections import deque
sys.setrecursionlimit(10 ** 9)

n, m = map(int, sys.stdin.readline().split(' '))

maps = []

for i in range(n):    
	maps.append(list(map(int, input().split())))


visited = [[-1] * m for _ in range(n)]



dx = [1, -1, 0, 0]
dy = [0, 0, 1, -1]

def dfs(x, y):
    if x == n-1 and y == m-1:
        return 1
    
    if visited[x][y] == -1:
        visited[x][y] = 0

        for direction in range(4):
            new_dx = x + dx[direction]
            new_dy = y + dy[direction]

            if 0 <= new_dx < n and 0 <= new_dy < m:    
                if maps[new_dx][new_dy] < maps[x][y]:
                    visited[x][y] += dfs(new_dx, new_dy)

    return visited[x][y]

                    




print(dfs(0, 0))

 

바뀐것은 visited를 DP로 활용하며 목적지에 방문한 루트를 또 방문하면 목적지에 방문한 것으로 가정하고 DFS를 끝내는 것이다.

이로써 중복된 방문을 피해 시간초과를 피할 수 있었다.

'알고리즘 > 백준' 카테고리의 다른 글

[백준] 14501. 퇴사 Python  (0) 2023.11.21
[백준] 2839. 설탕배달 Python  (1) 2023.11.20
[백준] 7576. 토마토 Python  (0) 2023.11.10
[백준] 16948. 데스 나이트 Python  (0) 2023.11.07
[백준] 1715. 카드 정렬하기 Python  (0) 2023.11.05