Longest Increasing Path in a Matrix
Question
Given an integer matrix, find the length of the longest increasing path.
From each cell, you can either move to four directions: left, right, up or down. You may NOT move diagonally or move outside of the boundary (i.e. wrap-around is not allowed).
Solution
核心想法不難,用 DFS 去遍歷然後更新 global variable 就可以。但用 naive DFS 會得到 TEL。看了解答後,發現由於 DFS 會重複計算很多點,因此可以使用 memorization 來提高效率,即用一個 int[][] 來記錄該點遍歷完後的 max,如果踩到這個點,直接返回值就可以 return
final int[][] steps = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
public int longestIncreasingPath(int[][] matrix) {
if (matrix.length == 0 || matrix == null) {
return 0;
}
int row = matrix.length, col = matrix[0].length;
int[][] cache = new int[row][col];
int max = 1;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
int len = dfs(matrix, i, j, row, col, cache);
max = Math.max(max, len);
}
}
return max;
}
public int dfs(int[][] matrix, int i, int j, int row, int col, int[][] cache) {
if (cache[i][j] != 0) {
return cache[i][j];
}
int max = 1;
for (int[] step : steps) {
int x = i + step[0], y = j + step[1];
if (x >= row || x < 0 || y >= col || y < 0 || matrix[x][y] <= matrix[i][j]) {
continue;
}
int len = 1 + dfs(matrix, x, y, row, col, cache);
max = Math.max(max, len);
}
cache[i][j] = max;
return max;
}
我用的方法是 public void dfs,也就是沒有 return 值,但不知道為什麼一直有 bug,記錄在下面,之後可以回來想。跟劉哥討論完,我下面的寫法有兩個錯誤:
- 當 cache[i][j] != 0 的時候,也應該要去 update cache,不能只在當 dfs 走不下去的時候才更新
- 在 cache[i][j] 的 len = Math.max(len, cache[i][j] + length - 1);但在 dfs 走不下去時的 cache[initial_i][initial_j] = Math.max(length, cahce[][]) 就好
不過這樣雖然對了,但還是會拿到 TLE。用這樣的寫法,即便是有了 cache,也只會在外層迭代時,譬如從 (1,0),dfs 走回 (0,0) 時可以拿值來用。但其實你在裡面 dfs 其他點,雖然都算過,但因為這個寫法並不會去記錄,所以相當於沒什麼 improvement,可能只比 naive DFS 好那麼一些而已。Memorization 的精髓在於讓每個點只計算一次,之後走到就可以直接拿來用,因此還是要讓自己習慣寫有 return 的 DFS
int len = 1;
public int longestIncreasingPath(int[][] matrix) {
if (matrix.length == 0 || matrix == null) {
return 0;
}
int[][] steps = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int[][] cache = new int[matrix.length][matrix[0].length];
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
dfs(matrix, steps, i, j, i, j, 1, cache);
}
}
return len;
}
public void dfs(int[][] matrix, int[][] steps, int i, int j, int initial_i, int initial_j, int length, int[][] cache) {
if (cache[i][j] != 0) {
len = Math.max(len, cache[i][j] + length);
return;
}
for (int[] step : steps) {
int x = i + step[0], y = j + step[1];
if (x >= matrix.length || x < 0 || y >= matrix[0].length || y < 0 || matrix[x][y] <= matrix[i][j]) {
len = Math.max(len, length);
cache[initial_i][initial_j] = Math.max(length - 1, cache[initial_i][initial_j]);
continue;
} else {
dfs(matrix, steps, x, y, initial_i, initial_j, length + 1, cache);
}
}
return;
}