이동 : 상하좌우로만 가능하다.
(0,0) —>(m-1,n-1)까지 이동 하려 한다.
높이가 더 낮은 지점으로만 이동하려고 한다.
이 때 이동할 수 있는 [경로의 개수 ] 를 구하기
문제 해결과정
public class Main{
public static int m,n;
public static int[][] cache;
public static int[][] table;
public static Queue<int[]> q = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
// height를 기준으로 내림차순
return o2[2]-o1[2];
}
});
public static int[][] dirs = new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
public static int path =0;
public static void Setting() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
m = Integer.parseInt(st.nextToken());
n = Integer.parseInt(st.nextToken());
table = new int[m][n];
cache = new int[m][n];
for(int row=0;row<m;row++){
st = new StringTokenizer(br.readLine());
for(int col=0;col<n;col++)
table[row][col] = Integer.parseInt(st.nextToken());
}
// 0,0 칸 캐시를 1로 해야, 인접한 칸까지의 경로에 수가 더해진다.
cache[0][0]=1;
// PQ를 세팅한다.
// 첫 번째 줄은, (0,0)칸만 제외하고 넣어준다.
for(int col=1;col<n;col++)
q.add(new int[]{0,col,table[0][col]});
for(int row=1;row<m;row++){
for(int col=0;col<n;col++){
q.add(new int[]{row,col,table[row][col]});
}
}
}
public static void dp(){
int curH=0;
while(q.isEmpty()==false){
int[] cur = q.poll();
int path=0;
for(int dir=0;dir<dirs.length;dir++){
int y = cur[0]+dirs[dir][0];
int x = cur[1]+dirs[dir][1];
// 벗어나면 fail
if(y<0||x<0||y>=m||x>=n)continue;
// 더 높은 칸이면 path개수를 더한다.
if(table[y][x]>cur[2]) path+=cache[y][x];
}
//System.out.println(cur[0]+","+cur[1] +" : "+path);
// cache를 업데이트
cache[cur[0]][cur[1]]=path;
}
}
public static void main(String[] args) throws IOException{
Setting();
dp();
System.out.println(cache[m-1][n-1]);
}
}
이게 내 방법보다 효율이 더 좋았다.
단순한 DFS를 이용하여 재귀적으로 찾아나간다면 , dfs를 이용한 백트랙킹 방식으로, 매우 많은 경우의 수를 다루게 되어 시간초과가 날 수 밖에 없다.
하지만 여기에 DP를 얹는다면 시간을 줄일 수 있다.
여기서는 cache[y][x]는 해당 칸 이 포함되는 경로의 개수를 의미한다.
만약, 어떤 경로를 통해서 [m,n]에 도달하는 경우, 해당 경로에 속하는 모든 노드들은 모두 [m,n]까지 도달 할 수 있는 것들이 된다.
또한 dfs의 경우, 모든 경로가 서로 다 다른 경로가 된다는 것을 기억하고 있으면 된다. (path의 구성이 모두 다르다)
dfs를 이용한 backtrack의 경우, 각 경로가 넘겨주는 값을 통하여 , 현재 노드가 포함된 경로의 개수를 알 수가 있다. (그림을 보면 이해될 듯 하다 )
따라서, 이런그림으로 이해하면 된다.
public class Main{
public static int m,n;
public static int[][] cache = new int[501][501];
public static int[][] table = new int[501][501];
public static int[][] dirs = new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
public static int path =0;
public static void Setting() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
m = Integer.parseInt(st.nextToken());
n = Integer.parseInt(st.nextToken());
for(int row=0;row<m;row++){
st = new StringTokenizer(br.readLine());
for(int col=0;col<n;col++)
table[row][col] = Integer.parseInt(st.nextToken());
}
// cache를 -1로 세팅한다. dfs를 통해서 cache에 0이 들어올 것도 존재함을 생각하라.
// dp는 왠만하면 -1로 세팅하고 시작한다.
for(int row=0;row<m;row++){
Arrays.fill(cache[row],-1);
}
}
public static int dfs(int y,int x){
//System.out.println(y+","+x);
if(x==n-1 &&y==m-1) return 1;
if(cache[y][x]!=-1) return cache[y][x];
int path =0;
for(int dir =0;dir<dirs.length;dir++){
int ny=y+dirs[dir][0];
int nx=x+dirs[dir][1];
// 범위를 넘으면 pass
if(ny<0||nx<0||ny>=m||nx>=n) continue;
// 더 낮은 곳이 아니면 pass
if(table[ny][nx]>=table[y][x])continue;
int temp = dfs(ny,nx);
path+=temp;
}
cache[y][x]=path;
return cache[y][x];
}
public static void main(String[] args) throws IOException{
Setting();
dfs(0,0);
System.out.println(cache[0][0]);
}
}