본문 바로가기
백준알고리즘/DFS 와 BFS

(Python/🥇3)백준알고리즘 1520번: 내리막 길

by windy7271 2023. 6. 10.
728x90
반응형

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

문제:

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

 

예시 1

 

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

 

예시 2

 

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

입력:

 

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

출력:

 

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

 

풀이:

이 문제는 dfs 만 사용하면 안되는 문제이다. dfs만 사용하면 이미 탐색한 곳을 다시 탐색하여 시간초과가 난다. 탐색한곳을 탐색처리하면 다시 방문처리할 수 없어 안된다. dp를 통해 각 위치에서에 갈 수 있는 수를 저장해놓는식으로 해야한다.

 


M,N = map(int, sys.stdin.readline().split(" "))
maps = [list(map(int,sys.stdin.readline().rstrip().split(" "))) for _ in range(M)]

visited = [[ 0 ] * N for _ in range(M)]
start = maps[0][0]; end = maps[M-1][N-1]
move = [(1,0),( -1,0),(0,-1),(0,1)] #상,하,좌,우
dp = [[-1 for _ in range(N)] for _ in range(M)]


def dfs(x, y):
    if x == M - 1 and y == N - 1:
        return 1

    if dp[x][y] == -1: # 탐색 안 했으면
        dp[x][y] = 0 # 탐색 한걸로 만들어줌
        for dx, dy in move:
            nx = x + dx
            ny = y + dy

            if 0 <= nx < M and 0 <= ny < N:
                if maps[x][y] > maps[nx][ny]:
                    dp[x][y] += dfs(nx, ny)

    return dp[x][y]
print(dfs(0, 0))

 

 

dp 를 모든 부분은 -1 방문 안 했다고 처리를 해둔다. 

그리고 탈출조건은 N-1,M-1 을 만났을때 탈출을 해준다.

 

똑같이 dfs를 돌리는데 만약에 dp[x][y] 가 탐색을 안 한 곳이면 0으로 만들어준다.

그리고 마지막에 dfs(nx,ny) 를 더해주는데 이 부분에서 깊게 들어가서 M-1,N-1 을 만나면 1을 리턴해준다.

즉 0,0 에서 3,4 를 가는 노선에 +1 을 해주는것이다.

 

처음

처음 코드를 돌리면 저 선 부분이 다 0 으로 바뀌고 return을 받으면서 (0,0) ~(2,3) 까지 1로 바뀐다 (2,4) 는 도착지점이기때문에 바뀌지 않는다. 

[[1, -1, -1, -1, -1], [1, -1, -1, -1, -1], [1, -1, -1, -1, -1], [1, 1, 1, 1, -1]]

이런식으로 말이다.

그러면 리턴으로 쭉 돌아오면 다시 (0,0) 에 부분이다. 그리고 아까 포문으로 재귀를 불렀을텐데, 아직 실행 안 한 재귀가 실행된다.

그림에서 밑으로 간게 끝나 이제 오른쪽으로 갈 차례이므로 오른쪽으로 실행한다.

 

탐색한 곳이거나 탐색할 수 없으면 자기자신을 리턴하면 된다. 

 

dp 어렵다

 

반응형

댓글