二维动态规划题目(未完)

本文介绍了如何解决力扣平台上的三个问题:最小路径和的暴力递归和记忆化优化、单词搜索的暴力递归搜索以及最长公共子序列的求解。通过记忆化策略和依赖关系分析,展示了提高算法效率的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      

     1.最小路径和(力扣LCR 99题)

        给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

        说明:一个机器人每次只能向下或者向右移动一步。

        方法一:暴力递归

    //暴力递归
    public static int minPathSum(int[][] grid){
        return f1(grid,grid.length-1,grid[0].length-1);
    }
    public static int f1(int[][] grid,int i,int j){
        if(i==0 && j==0){
            return grid[0][0];
        }
        int up=Integer.MAX_VALUE;//从上边的格子移动过来的
        int left=Integer.MAX_VALUE;//从左边的格子移动过来的
        if(i-1>=0){//判断不越界
            up=f1(grid,i-1,j);
        }
        if(j-1>=0){//判断不越界
            left=f1(grid,i,j-1);
        }
        return grid[i][j]+Math.min(up,left);
    }

        暴力递归思路很清晰但是时间复杂度会非常高。提交也会超时。

        因为此暴力递归中,存在大量的重复计算,因此,时间复杂度比较差。

        方法二:记忆化递归

        带有记忆化的递归,也就是将每次的计算结果存入到一个结构中,每次发生重复计算时,直接从存数据的结构中取数据即可,由此可以减少大量的重复计算,时间复杂度有了较大的优化。

class Solution {
    public static int minPathSum(int[][] grid){
        int n=grid.length;
        int m=grid[0].length;
        int[][] dp=new int[n][m];
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                dp[i][j]=-1;
            }
        }
        return f2(grid,grid.length-1,grid[0].length-1,dp);
    }
    public static int f2(int[][] grid, int i, int j, int[][] dp) {
        if(dp[i][j]!=-1){
            return dp[i][j];
        }
        int ans;
        if(i==0 && j==0){
            ans=grid[0][0];
        }else {
            int up=Integer.MAX_VALUE;//从上边的格子移动过来的
            int left=Integer.MAX_VALUE;//从左边的格子移动过来的
            if(i-1>=0){//判断不越界
                up=f2(grid,i-1,j,dp);
            }
            if(j-1>=0){//判断不越界
                left=f2(grid,i,j-1,dp);
            }
            ans=grid[i][j]+Math.min(up,left);
        }
        dp[i][j]=ans;
        return ans;
    }
}

        方法三:根据依赖关系推出

        每一个位置,只能从上或者左得到,只依赖上和左。

class Solution {
   //非递归,按照依赖关系求解
    public static int minPathSum(int[][] grid){
        int n=grid.length;
        int m=grid[0].length;
        int[][] dp=new int[n][m];
        dp[0][0]=grid[0][0];
        for(int i=1;i<n;i++){
            dp[i][0]=dp[i-1][0]+grid[i][0];
        }
        for(int j=1;j<m;j++){
            dp[0][j]=dp[0][j-1]+grid[0][j];
        }
        for(int i=1;i<n;i++){
            for(int j=1;j<m;j++){
                dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
            }
        }
        return dp[n-1][m-1];
    }
}

        方法四:依赖关系+空间压缩

        我们有时候没有必要,为了求二维数组中的某一个值而弄一个二维数组,如果压缩为一维数组,也能够解决问题的话,那么我们应该尽量采用一维数组的形式。

        我们用一维数组来表示第i行的结果,直到推到最后一行。

class Solution {
   //位置依赖+空间压缩
    public static int minPathSum(int[][] grid){
        int n=grid.length;
        int m=grid[0].length;
        int[] dp=new int[m];
        dp[0]=grid[0][0];
        for(int j=1;j<m;j++){
            dp[j]=dp[j-1]+grid[0][j];
        }
        for(int i=1;i<n;i++){
            dp[0]+=grid[i][0];
            for(int j=1;j<m;j++){
                dp[j]=Math.min(dp[j-1],dp[j])+grid[i][j];
            }
        }
        return dp[m-1];
    }
}

2.单词搜索(力扣79题)

        给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

        单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

        暴力递归搜索

        首先遍历二维数组,看当前字符是否是要查找单词的首字母,如果是,调用递归,否则跳过。

        然后就是递归过程,每次递归,都要向四个方向去搜索剩余字母,分别判断符合条件并调用相应的递归函数。

class Solution {
   public boolean exist(char[][] board, String word) {
        char[] charArray = word.toCharArray();
        boolean[][] flag=new boolean[board.length][board[0].length];
        for(int i=0;i<board.length;i++){
            for(int j=0;j<board[0].length;j++){
                if(board[i][j]==charArray[0]){
                    flag[i][j]=true;
                    if(f1(board,charArray,i,j,flag,0)){
                        return true;
                    }
                    flag[i][j]=false;
                }
            }
        }
        return false;
    }
    public boolean f1(char[][] board,char[] charArray,int i,int j,boolean[][] flag,int index){
        index++;
        if(index>=charArray.length){
            return true;
        }
        boolean isFait=false;
        if(i-1>=0 && !flag[i-1][j] && charArray[index]==board[i-1][j]){
            flag[i-1][j]=true;
            isFait=f1(board,charArray,i-1,j,flag,index) || isFait;
            flag[i-1][j]=false;
        }
        if(i+1<board.length && !flag[i+1][j] && charArray[index]==board[i+1][j]){
            flag[i+1][j]=true;
            isFait=f1(board,charArray,i+1,j,flag,index) || isFait;
            flag[i+1][j]=false;
        }
        if(j-1>=0 && !flag[i][j-1] && charArray[index]==board[i][j-1]){
            flag[i][j-1]=true;
            isFait=f1(board,charArray,i,j-1,flag,index) || isFait;
            flag[i][j-1]=false;
        }
        if(j+1<board[0].length && !flag[i][j+1] && charArray[index]==board[i][j+1]){
            flag[i][j+1]=true;
            isFait=f1(board,charArray,i,j+1,flag,index) || isFait;
            flag[i][j+1]=false;
        }
        return isFait;
    }
}

        3.最长公共子序列(力扣LCR 95题)

        给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

        一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

        两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值