https://www.acmicpc.net/problem/1520
1520번: 내리막 길
여행을 떠난 세준이는 지도를 하나 구하였다. 이 지도는 아래 그림과 같이 직사각형 모양이며 여러 칸으로 나뉘어져 있다. 한 칸은 한 지점을 나타내는데 각 칸에는 그 지점의 높이가 쓰여 있으
www.acmicpc.net
1. 문제 이해
제일 왼쪽 위 칸에서부터 제일 오른쪽 아래 칸으로 이동할 때 계속해서 높이가 낮아지도록 이동할 수 있는 내리막길로만 이동하는 경로의 개수를 구하는 문제입니다.
2. 문제 접근
첫번째 접근: DFS
단순하게 왼쪽 위 칸에서부터 오른쪽 아래 칸으로 이동을 DFS로 풀이하면 시간초과가 날 것이라고 생각했습니다. 그래서내리막 경로에 해당하는 칸들에 방문처리를 하고 만약에 탐색 도중 방문 처리된 칸을 만나면 경로의 수를 하나 늘려주는 방식으로 풀이하였습니다. 하지만 이 풀이는 미처 탐색하지 못하는 경로가 발생할 수 있어서 정해가 아닙니다.
4 4
16 9 8 1
15 10 7 2
14 11 6 3
13 12 5 4
두번째 접근: DP
예전에 비슷한 문제를 DP로 푼 기억이 있어서 DP로 접근해보았습니다. dp배열의 정의는 다음과 같습니다.
dp[i][j] = (i, j) 위치에 내리막길 경로로 도달 할 수 있는 경우의 수
가장 중요한 초기 조건은 제일 왼쪽 위인 dp[0][0]는 시작위치이므로 1이고 나머지 위치는 0에서 시작합니다. 그 다음 가장 높은 위치에서부터 4방향중 이동 가능한 위치에 현재 위치에 도달할 수 있는 경우의 수를 모두 더해주면 됩니다. 이것은 자신보다 높은 위치에 도달할 수 있는 경우는 모두 계산이 되어 있기 때문에 가능합니다.
3. 풀이
import sys
input = sys.stdin.readline
n, m = tuple(map(int, input().split()))
board = [
list(map(int, input().split()))
for _ in range(n)
]
order = [
(y, x) for y in range(n) for x in range(m)
]
# 가장 높은 곳부터 내림차순으로 졍렬
order.sort(key= lambda x : board[x[0]][x[1]], reverse=True)
dp = [
[0] * m
for _ in range(n)
]
dp[0][0] = 1
dys = [0, 1, 0, -1]
dxs = [1, 0, -1, 0]
def inRange(y, x):
return 0 <= x < m and 0 <= y < n
for y, x in order:
for dy, dx in zip(dys, dxs):
ny = y + dy
nx = x + dx
# 현재 자신보다 높은 곳은 계산이 되어있으므로 현재 높이에서 다음으로 이동할 수 있는 위치에
# 현재 위치로 올 수 있는 경우의 수를 더해주면 된다.
if inRange(ny, nx) and board[y][x] > board[ny][nx]:
dp[ny][nx] += dp[y][x]
print(dp[n - 1][m - 1])
4.회고
DFS로 접근하니 별의별 접근을 다해보았던 것 같습니다. 3차원 배열을 통해서 현재 위치에 어느 방향을 보면서 도달할지부터 visited[a][b][c][d] (a, b) -> (c, d)로 온 경우까지 다 접근해보았는데 뚜렷하게 풀이가 보이지 않아서 DP로 접근해보았는데 쉽게 풀 수 있었습니다.
5. 배운점
가끔 정렬하다 TypeError를 만나는 경우가 있는데 높은 확률로 order.sort(key=lambda ...)에서 key를 빼먹은 경우였습니다.
'문제 해결 > BOJ' 카테고리의 다른 글
[백준] 불우이웃돕기 (0) | 2023.04.23 |
---|---|
[백준] 박스 채우기 (0) | 2023.04.22 |
[백준] 택배 (0) | 2023.04.20 |
[백준] 로봇 (0) | 2023.04.19 |
[백준] ACM Craft (0) | 2023.04.18 |