본문 바로가기

알고리즘/백준

1520번 : 내리막 길(C++, 시간 초과, Bottom-up, Top-down)

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

 

1520번: 내리막 길

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

www.acmicpc.net

 

이번 문제는, 다이나믹 프로그래밍 문제이다.

대학생 때, Bottom-up 접근 방법, Top-down 접근 방법들을 배웠던 기억이 난다. 그러면서 호출되는 순서를 그래프로 표현해봐라는 과제가 있었는데, 하면서도 이걸 왜하나 싶었는데...

 

 

 

이제 그 중요성을 알 수 있었다.

 

 

 

또한, 지금까지 접근했던 문제들이 Bottom-up으로 모두 풀리던 문제들이여서, 이 문제를 만났을 때 접근 방식조차 생각해지 못했다 ㅠㅠ

 

 

 


 

 

 처음에 이 문제를 풀기 위해서, DFS 접근을 생각하고 모든 칸을 방문하며, 조건이 만족하면 1을 더해주도록 했다. 정답은 나오지만, 시간초과가 발생한다.

 

 

 

 

시간 초과가 발생하는 이유는, DFS는 Return하면서 방문했던 노드를 재 방문할 수 있다.

 

 

 

 

이 말이 무슨 말이냐면, 

 

 

 

 

 

이 그림을 보면, 20은 재방문이 된다. 만약에 20처럼 재방문이 되는 칸이 많아진다면 시간초과가 무조건 발생할 수 있다.

 

 

 

 

50,45,37,32에 대해서는 재방문되지 않는다.

 

 

 

 

M = 4, N = 5인 경우에 그래프를 그려보자.

 

 

 

 

 

위와 같은 그래프를 그려질 수 있다. 좌측에서부터 상, 하, 좌, 우이다. 5,5와 4,6은 범위를 초과하므로 제외해야 한다. 또한, 내리막길의 조건에 부합하는 것만 경로의 수로 더해질 수 있다.

 

 

 

 

함수를 정의할 때, 매개변수로 행과 열의 정보가 담을 수 있으면 된다.

 

 

 

 

DP를 이용한 재귀함수에서 정의해줘야 할 조건은 다음과 같다.

 

 

1. 종료 조건

2. 방문 확인(메모이제이션)

3. 호출 조건

 

 

 

위의 세가지를 완성해서 함수를 작성하면 된다.

 

 

 

 

여기서 Bottom-Up을 사용하지 않는 이유에 대해서 좀 적어보겠다.

 

 

 

DP[i][j]를 구하기 위해선, DP[i - 1][j], DP[i + 1][j], DP[i][j - 1], DP[i][j + 1]의 합을 구해야 한다. 일반적으로 우측 아래방향으로 배열을 읽는다면, DP[i + 1][j]와 DP[i][j + 1]의 값을 구하기 어렵다.

 

 

 

 

하향식 접근이 더 적합하다!

 

 

 


 

 

해설코드(C++).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <algorithm>
#include <queue>
 
using namespace std;
 
int map[501][501= { 0 };
int dp[501][501= { 0 };
int N, M;
int op[4][2= { {1,0},{-1,0},{0,1},{0,-1} };
 
int dfs(int i, int j) {
    if (i == 1 && j == 1)
        return 1;
 
    if (dp[i][j] != -1)
        return dp[i][j];
 
    dp[i][j] = 0;
    for (int o = 0; o < 4; o++) {
        int n_i = i + op[o][0];
        int n_j = j + op[o][1];
        if (n_i >= 1 && n_i <= M && n_j >= 1 && n_j <= N && map[n_i][n_j] > map[i][j])
            dp[i][j] += dfs(n_i, n_j);
    }
 
    return dp[i][j];
}
int main(void) {
    //freopen("input.txt", "r", stdin);
    cin >> M >> N;
    for (int i = 1; i <= M; i++) {
        for (int j = 1; j <= N; j++) {
            cin >> map[i][j];
            dp[i][j] = -1;
        }
    }
 
    cout << dfs(M, N) << endl;
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

 

 

 

'알고리즘 > 백준' 카테고리의 다른 글

1890번 : 점프  (0) 2020.01.31
11054번 : 가장 긴 바이토닉 부분 수열  (0) 2020.01.31
1904번 : 01타일  (0) 2020.01.27
15927번 : 회문은 회문아니야!!  (0) 2020.01.27
2167번: 2차원 배열의 합  (0) 2020.01.27