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

 

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

 

1520번: 내리막 길

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

www.acmicpc.net


 

 

해결전략

 

깊이우선탐색 + 다이나믹 프로그래밍, Dynamic Programming

DFS + DP (= 메모이제이션)

 

DFS로만 풀면 시간초과가 나온다.


 

 

DFS 풀이:  시간초과

 

#include <iostream>
#include <vector>
using namespace std;
int dirY[4] = { -1, 0, 1, 0 };
int dirX[4] = { 0, -1, 0, 1 };
int r, c; // 행, 열
int answer; // 경로의 개수
vector<vector<int>> v;
vector<vector<int>> ch;
void DFS(int y, int x){
if (y == r - 1 && x == c - 1) {
answer++;
return;
}
for (int i = 0; i < 4; i++) {
int ny = y + dirY[i];
int nx = x + dirX[i];
if (ny < 0 || r <= ny || nx < 0 || c <= nx) continue;
if (ch[ny][nx] == 0 && v[ny][nx] < v[y][x])
{
ch[ny][nx] = 1;
DFS(ny, nx);
ch[ny][nx] = 0;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> r >> c;
v.resize(r, vector<int>(c));
ch.resize(r, vector<int>(c));
for (int y = 0; y < r; y++) {
for (int x = 0; x < c; x++) {
cin >> v[y][x];
}
}
DFS(0, 0);
cout << answer;
return 0;
}

 


 

 

정답코드:  DFS + DP (= 메모이제이션)

 

 도착 지점으로부터 거꾸로 시작 지점으로 경로를 추적하면서 경로의 수를 계산한다!

ch[ y ][ x ]의 값은 해당 위치(y, x)에서 목적지(r-1, c-1)까지의 경로의 수다.

 

#include <iostream>
#include <vector>
using namespace std;
int dirY[4] = { -1, 0, 1, 0 };
int dirX[4] = { 0, -1, 0, 1 };
int r, c; // r: 행, c: 열
long long answer; // 경로 개수
vector<vector<int>> v;
vector<vector<int>> ch; // 방문 처리 & 경로 개수 캐싱. -1은 미방문 상태. ch배열 원소들의 값은 해당 위치(y, x)에서 목적지(r-1, c-1)까지 경로 개수.
void DFS(int y, int x){
if (y == r - 1 && x == c - 1) return; // 도착 지점에 도달했을 경우
if (ch[y][x] != -1) return; // 이미 방문한 경우(=경로개수 이미 계산됨).
ch[y][x] = 0; // (y, x) 방문표시. 아래에서 경로개수 계산.
for (int i = 0; i < 4; i++) {
int ny = y + dirY[i];
int nx = x + dirX[i];
if (ny < 0 || r <= ny || nx < 0 || c <= nx) continue;
if (v[y][x] <= v[ny][nx]) continue;
if (ch[ny][nx] == -1){ // 아직 방문한적이 없다면 DFS 탐색.
//cout << ny << ", " << nx << "\n";
DFS(ny, nx);
ch[y][x] += ch[ny][nx]; // 탐색 후 현재 위치의 경로개수 업데이트
//cout << "Calculation: " << ny << ", " << nx << "\n";
}
else { // 방문한적이 있는 경우
ch[y][x] += ch[ny][nx]; // 이미 계산된 경로 수를 현재 위치에 추가
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> r >> c;
v.resize(r, vector<int>(c));
ch.resize(r, vector<int>(c, -1)); // -1을 초기값으로 설정. -1은 미방문 상태.
for (int y = 0; y < r; y++) {
for (int x = 0; x < c; x++) {
cin >> v[y][x];
}
}
// 이 코드에서 도착점은 경로 계산의 기준점이다. 도착 지점에서 시작하여 거꾸로 이동하면서 경로의 수를 계산한다.
// 도착 지점의 경로 수를 1로 설정함으로써, 모든 경로 계산의 출발점이 된다.
ch[r-1][c-1] = 1;
DFS(0, 0); // 시작 지점에서 DFS 시작
cout << ch[0][0]; // 시작지점(0, 0)에서 도착지점(r-1, c-1)까지의 경로의 수
return 0;
}

 

 

 

[ 반례 예시 ]

 

3 3

9 4 3

8 5 2

7 6 1

답: 6

 

4 4

93 72 61 58

90 73 19 49

85 36 75 13

21 41 45  7

답: 2