알고리즘/백준

[백준/C,C++] 1520번: 내리막 길

이민훈 2021. 7. 4. 23:23

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

 

1520번: 내리막 길

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

www.acmicpc.net

풀이

해당 문제를 보고 DFS, BFS문제구나 생각을 했습니다.

 

관건은 동적계획법을 사용해 어떻게 연산 수를 줄이는 것인가인데

 

다른 고수분들의 풀이를 보고 참고해 풀었습니다.

 

DP[y][x] = 현재 지점의 좌표가 y, x일 때, 끝지점에 도달할 수 있는 경로의 개수로 가정하고

 

이를 사용한다면 탐색의 횟수를 줄일 수 있습니다.

 

 

예제를 통해 적용해보겠습니다.

 

 

최초 출발 지점인 50에서 10으로 갈 수 있는 경로의 개수는

 

35 그리고 45에서 갈 수 있는 경로의 개수를 합친 것과 같습니다.

 

35에서는 갈 수 있는 경로의 개수가 1개, 45에서는 2개이므로 50에서 10으로 갈 수 있는 경로의 개수는 3개 인 것이죠.

 

 

결과적으로 DP 배열은 위와 같이 채워진다고 보시면 됩니다.

 

첫 번째 경로를 살펴보죠.

 

목적지에 도달한 경우는 기저 사례이고 1을 반환합니다.

 

그리고 50에서부터 지나온 DP[y][x]에는 하나의 경로가 존재하게 됩니다.

 

 

두 번째 경로는 기저 사례에 도달하기 전 4, 4 좌표인 15를 만나 1을 반환받게 됩니다.

 

1,4 좌표인 32에서는 하나의 경로가 더 존재하는데 이 역시 2, 4 좌표인 20을 만나 1을 반환받고

 

32에는 2가지 경로가 존재함을 알 수 있습니다.

 

즉 DP[y][x]는 y, x 좌표에서 갈 수 있는 좌표들이 가진 경로의 개수 합이라고 보면 됩니다.

 

DP[1][1]은 DP[2][1] + DP[1][2]일 것이고, DP[1][4]는 DP[2][4] + DP[1][5]일테죠.

 

 

이 과정에서 목표지점으로 갈 수 있는 경로의 개수가 확인된 좌표에서는 더 이상 탐색하지 않고

 

해당 지점의 경로 개수를 반환하게 되면서 연산의 수가 매우 줄어들게 됩니다.

 

#include <iostream>
#include <cstring>

using namespace std;

const int MAX = 501;
const pair<int, int> direction[4] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };

int M, N;
int map[MAX][MAX];
int cache[MAX][MAX];

int DFS(int y, int x)
{
    // 기저 사례 (목적지에 도달)
    if (y == M && x == N) {
        return 1;
    }

    // 방문하지 않은 지점이라면 계속 탐색
    if (cache[y][x] == -1) {
        cache[y][x] = 0;
        for (int i = 0; i < 4; i++) {
            int ny = y + direction[i].first;
            int nx = x + direction[i].second;
            if (map[ny][nx] != 0 && map[y][x] > map[ny][nx]) {
                cache[y][x] += DFS(ny, nx);
            }
        }
    }

    // 방문한 곳이라면 해당 좌표에서 목적지까지 도달할 수 있는 경로의 개수 반환
    return cache[y][x];
}

int main(void)
{
    cin >> M >> N;

    memset(cache, -1, sizeof(cache));
    for (int i = 1; i <= M; i++) {
        for (int j = 1; j <= N; j++) {
            cin >> map[i][j];
        }
    }

    cout << DFS(1, 1);

    return 0;
}

반례

Input

5 5
10000 9142 2314 9554 9000
8777 5654 1324 2344 7566
7654 4300 1100 1580 6543
4800 3999 2999 1999 2000
9999 300 456 991 44

Output

4