하향식(Top-down) 풀이
아이디어
dfs(y, x)
가 좌표 (0, 0)~(y, x)에 대한 경로의 경우의 수를 구하는 함수라 하자.
- 이때
dfs(y, x)
는 4방향의 주변 칸 중 내리막으로 내려오는 칸 (y+dy[d], x+dx[d])
에 대한 dfs
값의 합이다.
- 계산이 중복될 수 있는 부분은 memoization해두자.
코드
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
static BufferedReader rd = new BufferedReader(new InputStreamReader(System.in));
static StringBuilder sb = new StringBuilder();
static StringTokenizer tk = null;
static int[] dy = {-1, 0, 1, 0};
static int[] dx = {0, 1, 0, -1};
static int M, N;
static int[][] map;
static int[][] memo;
public static void main(String[] args) throws Exception {
tk = new StringTokenizer(rd.readLine());
M = Integer.parseInt(tk.nextToken());
N = Integer.parseInt(tk.nextToken());
map = new int[M][N];
memo = new int[M][N];
for (int y=0; y<M; y++) {
tk = new StringTokenizer(rd.readLine());
for (int x=0; x<N; x++) {
map[y][x] = Integer.parseInt(tk.nextToken());
memo[y][x] = -1;
}
}
memo[0][0] = 1;
System.out.println(dfs(M-1, N-1));
}
static int dfs(int y, int x) {
if (memo[y][x] != -1) return memo[y][x];
int sum = 0;
for (int d=0; d<4; d++) {
int ny = y + dy[d];
int nx = x + dx[d];
if (ny < 0 || ny >= M || nx < 0 || nx >= N) continue;
if (map[ny][nx] <= map[y][x]) continue;
sum += dfs(ny, nx);
}
return memo[y][x] = sum;
}
}
메모리 및 시간
상향식(Bottom-up) 풀이
아이디어
- (0, 0)를 통해 내리막이 되는 칸들을 BFS로 탐색한다.
- 이때, 갈림길이 합쳐지는 교차점이 있다면, 교차점을 지나기 전에 각 갈림길 위의 점들을 모두 지나야하므로 순서를 지정해주기 위해 PriorityQueue을 이용하자.
- 갈림길이 합쳐질 때 경우의 수를 합치기 위해 모든 점에서의 값을 누적해 저쟝해야 한다. 여기서의
memo
는 그것이라 하자.
코드
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
static BufferedReader rd = new BufferedReader(new InputStreamReader(System.in));
static StringBuilder sb = new StringBuilder();
static StringTokenizer tk = null;
static int[] dy = {-1, 0, 1, 0};
static int[] dx = {0, 1, 0, -1};
static int M, N;
static int[][] map;
static int[][] memo;
static boolean[][][] enqueued;
static PriorityQueue<Coord> pq = new PriorityQueue<>();
public static void main(String[] args) throws Exception {
tk = new StringTokenizer(rd.readLine());
M = Integer.parseInt(tk.nextToken());
N = Integer.parseInt(tk.nextToken());
map = new int[M][N];
enqueued = new boolean[M][N][4];
memo = new int[M][N];
for (int y=0; y<M; y++) {
tk = new StringTokenizer(rd.readLine());
for (int x=0; x<N; x++) {
map[y][x] = Integer.parseInt(tk.nextToken());
}
}
memo[0][0] = 1;
pq.offer(new Coord(0, 0));
while (!pq.isEmpty()) {
Coord coord = pq.poll();
int y = coord.y;
int x = coord.x;
for (int d=0; d<4; d++) {
int ny = y + dy[d];
int nx = x + dx[d];
if (ny < 0 || ny >= M || nx < 0 || nx >= N) continue;
if (enqueued[y][x][d]) continue;
if (map[y][x] <= map[ny][nx]) continue;
memo[ny][nx] += memo[y][x];
enqueued[y][x][d] = true;
pq.offer(new Coord(ny, nx));
}
}
System.out.println(memo[M-1][N-1]);
}
static class Coord implements Comparable<Coord> {
int y, x;
Coord(int y, int x) {
this.y = y;
this.x = x;
}
@Override
public int compareTo(Coord o) {
return -Integer.compare(map[y][x], map[o.y][o.x]);
}
}
}
메모리 및 시간
리뷰
- 얼떨결에 두 가지 풀이로 푼 문제인데, 상향식으로 먼저 풀고 하향식 풀이를 뒤늦게 떠올렸기 때문이었다. 상향식 풀이에만 너무 익숙해져 하향식 풀이를 적용하지 못한 사례. 상황에 따라 고루 사용할 수 있도록 하자.
- 상향식 풀이는 크게 실용적이진 않은 것 같다.