Leetcode 329. 矩陣中的最長遞增路徑
矩陣中的最長遞增路徑
給定一個 m x n
的整數矩陣 matrix
,找出其中最長遞增路徑的長度。對於每個單元格,你可以往上、下、左、右四個方向移動。你不能在對角線方向上移動或移動到邊界外。
題解:記憶化搜尋
我們使用記憶化搜尋(Memoization)結合深度優先搜尋(DFS)來解決這個問題。具體步驟如下:
步驟
-
狀態定義:
- 定義 ( f[i][j] ) 表示從單元格 ( (i, j) ) 開始的最長遞增路徑的長度。
-
狀態轉移方程:
-
對於每個單元格 ( (i, j) ),考慮所有可以前往的單元格 ( (a, b) )。狀態轉移方程為:
f[i][j] = max(f[i][j], f[a][b] + 1)
這意味著如果可以從 ( (i, j) ) 移動到 ( (a, b) ),那麼從 ( (i, j) ) 開始的最長路徑至少是從 ( (a, b) ) 開始的最長路徑加一。
-
-
初始化:
- 初始化所有的 ( f[i][j] ) 為 (-1),表示每個單元格開始的最長路徑尚未計算。
-
遞迴搜尋:
- 對於每個單元格 ( (i, j) ),使用深度優先搜尋(DFS)計算從該單元格開始的最長遞增路徑,並利用記憶化搜尋儲存和重用之前計算的結果。(其實就是visit陣列)
實現
public class Solution {
private int[][] matrix;
private int[][] cache;
private int n;
private int m;
private int[] dx = {-1, 1, 0, 0}; // 方向陣列,表示上下左右
private int[] dy = {0, 0, 1, -1}; // 方向陣列,表示上下左右
public int longestIncreasingPath(int[][] matrix) {
if (matrix == null || matrix.length == 0) return 0;
this.matrix = matrix;
this.n = matrix.length;
this.m = matrix[0].length;
this.cache = new int[n][m];
int res = 0;
// 遍歷每個單元格
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 找到從每個單元格開始的最長路徑
res = Math.max(res, dfs(i, j));
}
}
return res;
}
private int dfs(int x, int y) {
// 如果快取中已有結果,直接返回
if (cache[x][y] != 0) return cache[x][y];
int max = 1; // 當前單元格的最長路徑長度
// 訪問四個方向
for (int i = 0; i < 4; i++) {
int nx = x + dx[i], ny = y + dy[i];
// 檢查新位置是否合法以及是否遞增
if (nx < 0 || nx >= n || ny < 0 || ny >= m || matrix[nx][ny] <= matrix[x][y]) continue;
// 計算從新位置開始的路徑長度
int len = 1 + dfs(nx, ny);
// 更新最長路徑長度
max = Math.max(max, len);
}
// 快取結果
cache[x][y] = max;
return max;
}
}
huawei2023112903
給定一個整數 ( n ),構造由值為 1 到 ( n ) 的節點組成的二叉搜尋樹,求出高度不超過 ( k ) 的不同二叉搜尋樹的數量(根節點高度為 1)。
輸入描述
- 兩個整數 ( n ) 和 ( k ),滿足 ( 1 \leq n, k \leq 35 )。
輸出描述
- 一個整數,表示不同二叉搜尋樹的數量。
示例
輸入:
5 4
輸出:
26
思路
定義 ( cache[i][j] ) 為在有 ( i ) 個節點,並且樹的高度不超過 ( j ) 的情況下,可以構造的二叉查詢樹的數量。
可以使用動態規劃或者記憶化搜尋來實現。
具體步驟如下:
-
初始狀態:
- 如果 ( k ) 為 0 且 ( n \neq 0 ),表示樹的高度已經超過了 ( k ),那麼不能再構造出新的樹,返回 0。
- 如果 ( k ) 為 0 且 ( n ) 也為 0,表示沒有節點且高度為 0,也只能構造出一棵空樹,返回 1。
- 如果 ( n ) 為 0,表示沒有節點,那麼只能構造出一棵空樹,返回 1。
-
遞迴計算:
- 對於每個 ( n ),可以分別列舉給左子樹分配 ( [1:n-1] ) 個節點,右子樹分配 ( [n-1:0] ) 個節點,並將對應的方案累乘,即為當前的方案數,累加當前方案數即為最終答案。
-
記憶化搜尋:
- 為了避免重複計算,需要使用記憶化搜尋。定義
dfs(cnt, dep)
函式表示當前有 cnt 個節點,樹的高度為 dep 時,能構造出的二叉查詢樹的數量。 - 如果之前已經計算過
dfs(cnt, dep)
,則直接返回快取的結果。
- 為了避免重複計算,需要使用記憶化搜尋。定義
import java.util.Scanner;
public class Main {
// Define the maximum size for cache
static final int N = 40;
// Initialize cache for memoization k<=35
static int[][] cache = new int[N][N];
// Depth-first search function for memoization
static int dfs(int n, int k) {
// If the result for (n, k) is already calculated, return it directly from cache(memory search)
if (cache[n][k] != -1) return cache[n][k];
// Base cases
if (k == 0 && n == 0) return 1; // If both k and n are 0, return 1 as there's only one empty tree
if (k == 0 && n != 0) return 0; // If k is 0 but n is not, return 0 as it's impossible to construct a tree
if (n == 0) return 1; // If n is 0, return 1 as there's only one empty tree
int res = 0;
// Recursively calculate the number of BSTs
for (int i = 1; i <= n; ++i) {
res += dfs(i - 1, k - 1) * dfs(n - i, k - 1);
}
// Cache the result for (n, k)
return cache[n][k] = res;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Read input values for n and k
int n = scanner.nextInt();
int k = scanner.nextInt();
// Initialize cache with -1
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
cache[i][j] = -1;
}
}
// Calculate and print the result
System.out.println(dfs(n, k));
}
}