本刷题专栏记录自己的记忆化搜索的做题。
576. 出界的路径数
题目链接及描述:
https://leetcode.cn/problems/out-of-boundary-paths/description/
第一次看到这个题目可能先入为主了,首先想到的是使用动态规划做,但动态规划一般都是单方向的,而这道题目可以重复进入(这点就是记忆化所在)可以参考三叶的https://leetcode.cn/problems/out-of-boundary-paths/solutions/936439/gong-shui-san-xie-yi-ti-shuang-jie-ji-yi-asrz/,这道题目使用动态规划还是有点难的,采用题解中提供的记忆化搜索的方法。后面再把可重复动态规划学习一下。
class Solution {
int MOD = (int)1e9+7;
int m, n, max;
int[][] dirs = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};
int[][][] cache;
public int findPaths(int _m, int _n, int _max, int r, int c) {
m = _m; n = _n; max = _max;
cache = new int[m][n][max + 1];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k <= max; k++) {
cache[i][j][k] = -1;
}
}
}
return dfs(r, c, max);
}
int dfs(int x, int y, int k) {
if (x < 0 || x >= m || y < 0 || y >= n) return 1;
if (k == 0) return 0;
if (cache[x][y][k] != -1) return cache[x][y][k];
int ans = 0;
for (int[] d : dirs) {
int nx = x + d[0], ny = y + d[1];
ans += dfs(nx, ny, k - 1);
ans %= MOD;
}
cache[x][y][k] = ans;
return ans;
}
}
486. 预测赢家
我首先思考到了一种最直观的想法,分别用两个变量记录两个人的得分,最终比较得分大小关系:
玩家1取数,可以取nums[i],此时玩家2在剩余的 [i + 1, j] 内取数。也分为两种情况。
玩家1取数,可以取nums[j],此时玩家2在剩余的 [i, j + 1] 内取数。也可以分为两种情况。
在递归过程中使用标记 flag 比较玩家1 或者 玩家 2 取数。
代码如下:
需要注意这种解法下,在玩家1取数时,玩家1 取左边届或右边界 任意一个能成功即可。但对于玩家 2 取数时,此时玩家 1 想赢则无论玩家 2 取左边界或右边界都能够获胜才可以。否则,玩家2均可以操作另一种获胜方式使得玩家1不能赢得比赛。
记忆化搜索:
相对分数 说成 净胜分 ,语义会更强一些。
甲乙比赛,甲先手面对区间[i…j]时,dp[i][j]表示甲对乙的净胜分。
最终求的就是,甲先手面对区间[0…n-1]时,甲对乙的净胜分dp[0][n-1]是否>=0。
甲先手面对区间[i…j]时,
1.如果甲拿nums[i],那么变成乙先手面对区间[i+1…j],这段区间内乙对甲的净胜分为dp[i+1][j];那么甲对乙的净胜分就应该是nums[i] - dp[i+1][j]。
2.如果甲拿nums[j],同理可得甲对乙的净胜分为是nums[j] - dp[i][j-1]。
以上两种情况二者取大即可。
参考代码如下:
class Solution {
public boolean predictTheWinner(int[] nums) {
int len = nums.length;
int[][] memo = new int[len][len];
for(int[] mem : memo){
Arrays.fill(mem, Integer.MIN_VALUE);
}
return getScores(nums, 0, len - 1, memo) >= 0;
}
public int getScores(int[] nums, int i, int j, int[][] memo){
if(i > j){
return 0;
}
if(memo[i][j] != Integer.MIN_VALUE){
return memo[i][j];
}
int leftScores = nums[i] - getScores(nums, i + 1, j, memo);
int rightScores= nums[j] - getScores(nums, i, j - 1, memo);
memo[i][j] = Math.max(leftScores, rightScores);
return memo[i][j];
}
// }
}
329. 矩阵中的最长递增路径
题目链接:329. 矩阵中的最长递增路径
这个题目在归类中分为了动态规划的题目,首先沿着动态规划思路思考,总是思考不全所有的情况,后面吃饭时候想到也许可以用dfs+记忆化搜索试一试,本质上这个题目不用记忆化搜索,仅仅使用dfs肯定也是能做的,但肯定会超时,所以加上记忆化。
加上记忆化后,dfs的返回值此时设置为当前节点matrix[i][j]能够取得的最大递增深度,获得最大深度后将其保存到memo数组中,后续dfs过程中遇到下标i,j对应的memo已经初始化过了,则直接返回。减少无效重复搜索。
代码如下:
class Solution {
int m, n;
int[][] memo;
int[][] neighbors = new int[][]{{0, 1},{0, -1},{1, 0},{-1, 0}};
public int longestIncreasingPath(int[][] matrix) {
this.m = matrix.length; this.n = matrix[0].length;
memo = new int[m][n];
int ans = Integer.MIN_VALUE;
for(int i = 0; i < m; i++){
Arrays.fill(memo[i], -1);
}
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
ans = Math.max(ans, dfs(matrix, 1, i, j));
}
}
return ans;
}
public int dfs(int[][] matrix, int nodeNums, int i, int j){
if(i < 0 || i >= m || j < 0 || j >= n){
return 0;
}
if(memo[i][j] != -1){
return memo[i][j];
}
int curNums = nodeNums;
for(int k = 0; k < 4; k++){
int curx = i + neighbors[k][0], cury = j + neighbors[k][1];
if(curx < 0 || curx >= m || cury < 0 || cury >= n || matrix[i][j] <= matrix[curx][cury]){
continue;
}
int tempNums = dfs(matrix, 1, curx, cury);
curNums = Math.max(curNums, nodeNums + tempNums);
}
memo[i][j] = curNums;
return curNums;
}
}
// 删除无效的判断条件后代码:
class Solution {
int m, n;
int[][] memo;
int[][] neighbors = new int[][]{{0, 1},{0, -1},{1, 0},{-1, 0}};
public int longestIncreasingPath(int[][] matrix) {
this.m = matrix.length; this.n = matrix[0].length;
memo = new int[m][n];
int ans = Integer.MIN_VALUE;
for(int i = 0; i < m; i++){
Arrays.fill(memo[i], -1);
}
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
ans = Math.max(ans, dfs(matrix, 1, i, j));
}
}
return ans;
}
public int dfs(int[][] matrix, int nodeNums, int i, int j){
// if(i < 0 || i >= m || j < 0 || j >= n){
// return 0;
// }
if(memo[i][j] != -1){
return memo[i][j];
}
int curNums = nodeNums;
for(int k = 0; k < 4; k++){
int curx = i + neighbors[k][0], cury = j + neighbors[k][1];
if(curx < 0 || curx >= m || cury < 0 || cury >= n || matrix[i][j] <= matrix[curx][cury]){
continue;
}
int tempNums = dfs(matrix, 1, curx, cury);
curNums = Math.max(curNums, nodeNums + tempNums);
}
memo[i][j] = curNums;
return curNums;
}
}