LC07:记忆化搜索

本刷题专栏记录自己的记忆化搜索的做题。

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. 预测赢家

题目链接:https://leetcode.cn/problems/predict-the-winner/description/?envType=study-plan-v2&envId=2024-spring-sprint-100

在这里插入图片描述
我首先思考到了一种最直观的想法,分别用两个变量记录两个人的得分,最终比较得分大小关系:

玩家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;
    } 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值