BAEKJOON #1520 내리막 길 (DFS, BFS) - python

nathan·2021년 9월 30일
1

알고리즘문제

목록 보기
69/102

내리막 길

출처 : 백준 #1520

시간 제한메모리 제한
2초128MB

문제

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

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



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


입력

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


출력

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


입출력 예시

예제 입력 1

4 5
50 45 37 32 30
35 50 40 20 25
30 30 25 17 28
27 24 22 15 10

예제 출력 1

3


풀이

생각 및 풀이 설명

  • 1. BFS & Priority Queue

    • BFS로 탐색하면서 maxheap에 다음 탐색 장소를 추가한다.
    • maxheap은 해당 칸의 크기가 큰 순서대로 나열한다.
    • 지날 때는 visited[nx][ny]가 0인지 체크하여 0이라면 maxheap에 추가하고 visited[x][y]의 값을 더해준다.
    • 만약 visited[nx][ny]가 0이 아니라면, 그냥 visited[x][y]의 값을 더해준다.
    • 이는 중복하여 같은 길을 지날 때는 더이상 탐색하지 않고 횟수만 늘려주어 전체적인 시간 복잡도를 낮추는 결과를 가져온다.
    • 아이디어 참고 : BFS & Priority Queue
  • 2. DFS & DP(Top-down)

    • dp는 초기 값을 -1로 설정한 2차원 배열로 둔다. (visited랑 비슷한 역할을 한다고 생각하면 된다.)
    • Top-down + Recursion 방식을 사용하기 때문에 (n-1, m-1)부터 거꾸로 (0, 0)까지 간다.
    • 만약 dp 값이 -1이라면(한 번도 방문되지 않았다면), 0으로 만들고 상,하,좌,우를 탐색하여 높은 칸이 있을 때 dfs를 사용한다(Recursion), 그리고 그 재귀 결과를 dp값에 더해준다.
    • 만약 dp 값이 -1이 아니라면, 이미 방문이 되었다는 뜻이므로 dp값을 그대로 return한다.
  • 마무리하며

    • 문제의 논리를 생각하는 것은 그다지 어렵지 않았으나, 이를 주어진 시간 범위 내에서 구현하는 것은 다른 문제였다.
    • 이번에 깨닫게 된 것은 BFS와 우선순위 큐와의 조합, DFS와 DP와의 조합이다.
    • 이렇듯 다양한 방법을 통해 조합하여 시간 복잡도를 낮출 수 있는 방법을 알아보았다는 데에 큰 의의를 둔다.

Python code (python3 - BFS & Priority Queue)

# 백준 1520번 내리막 길
from sys import stdin
from collections import deque
from heapq import heappush, heappop, heapify
input = stdin.readline
m, n = map(int, input().split())
matrix = []
for _ in range(m):
    matrix.append(list(map(int, input().split())))

count = 0

# 동 남 서 북
dx = [0, +1, 0, -1]
dy = [+1, 0, -1, 0]
visited = [[0]*n for _ in range(m)]
def bfs(now, x, y):
    q = []
    heapify(q)
    global count
    global matrix
    global visited
    visited[x][y] = 1
    heappush(q, (-now, x, y))
    while q:
        now, x, y = heappop(q)
        now *= -1
        if x == m-1 and y == n-1:
            continue
        for i in range(4):
            nx, ny = x + dx[i], y + dy[i]
            if 0 <= nx < m and 0 <= ny < n:
                temp = matrix[nx][ny]
                if temp < now:    # 더 낮은 계단칸
                    if visited[nx][ny] == 0:
                        heappush(q, (-temp, nx, ny))
                    visited[nx][ny] += visited[x][y]
    return 

bfs(matrix[0][0], 0, 0)
print(visited[m-1][n-1])

Python code (python3 - DFS & DP)

# 백준 1520번 내리막 길
import sys
from collections import deque
sys.setrecursionlimit(10**6)
input = sys.stdin.readline
m, n = map(int, input().split())
matrix = []
for _ in range(m):
    matrix.append(list(map(int, input().split())))

count = 0

# 동 남 서 북
dx = [0, +1, 0, -1]
dy = [+1, 0, -1, 0]
dp = [[-1]*n for _ in range(m)]
dp[0][0] = 1
def dfs(x, y):
    global count
    global matrix
    global dp
    now = matrix[x][y]
    if x == 0 and y == 0:
        return 1
    if dp[x][y] == -1:      # 핵심 dp 초기값을 0으로 두고 시작해버리면 겹치는 부분이 많아짐 
        dp[x][y] += 1
        for i in range(4):
            nx, ny = x + dx[i], y + dy[i]
            if 0 <= nx < m and 0 <= ny < n:
                temp = matrix[nx][ny]
                if temp > now:    # 더 낮은 계단칸
                    dp[x][y] += dfs(nx, ny)
    # 이미 방문이 되었다면, dp값을 그대로 return
    return dp[x][y]


result = dfs(m-1, n-1)
# print(dp)
print(result)
profile
나는 날마다 모든 면에서 점점 더 나아지고 있다.

0개의 댓글