记忆化搜索
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));
}
}