[BOJ] 1520 내리막길
DP, DFS
이 문제는 왼쪽 상단에서 오른쪽 하단까지 가는 경로의 개수를 구하는 문제이다. 단 경로의 조건은 현재 위치보다 숫자가 작은 인접 칸으로만 이동할 수 있다는 것이다.
그래서 이 문제의 조건은 위 내용 단 하나이고 시작, 끝 위치가 정해져있다는 점이다.
따라서 문제를 어떤 방식으로 풀어야 하는가를 고민해봤을때는, DFS, 즉 경로를 끝가지 가서 오른쪽 하단이 나오는지부터 탐색을 해봐야 한다.
- DFS를 (0, 0) 좌표를 시작점으로 두고 시작
DFS
- 만약 현재 좌표가 우측 하단에 도착했다면 1을 반환 (경로가 있다는 뜻)
- 아니라면 상,하,좌,우 를 탐색
- 인덱스 범위 밖이라면 pass
- 내리막길이 아니라면 pass
- 다음 좌표로 다시 DFS 시작 → 2로 돌아감
- 하위 sub problem에서 나온 반환 값(경로의 개수) 의 합을 반환. = 현재 내 problem의 경로 최댓값
이렇게 코드를 짜면 정말 많은 중복 문제들이 발생한다. 따라서 DFS, 재귀 방식으로 문제를 구현할 때 부분 중복 문제를 제거할 수 있는 메모이제이션(memoization)을 사용해서 시간 복잡도를 줄인다.
이를 모두 적용한 코드는 다음과 같다.
구현
import sys
def dfs(x, y, des_x, des_y):
if x == des_x and y == des_y:
mem[x][y] = 1
return mem[x][y]
answer = 0
for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
# 인덱스 범위 밖
if nx < 0 or ny < 0 or nx > des_x or ny > des_y:
continue
# 내리막길이 아니면
if graph[nx][ny] >= graph[x][y]:
continue
# 메모이제이션 된 값이 없다면
if mem[nx][ny] == -1:
mem[nx][ny] = dfs(nx, ny, des_x, des_y)
answer += mem[nx][ny]
return answer
if __name__ == "__main__":
sys.setrecursionlimit(100000)
M, N = map(int, sys.stdin.readline().rstrip().split())
graph = []
mem = [[-1]*N for _ in range(M)]
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
for _ in range(M):
graph.append(list(map(int, sys.stdin.readline().rstrip().split())))
print(dfs(0, 0, M-1, N-1))