动态规划-台阶与矩阵

这篇博客探讨了动态规划在解决数组台阶、矩阵路径和障碍路径问题中的应用,包括斐波那契数列、不同步数的台阶以及花费最小成本的台阶路径。通过优化代码,展示了如何简洁地实现这些算法,如最小成本爬楼梯和唯一路径计算,并讨论了带障碍物的情况。

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

斐波那契和台阶

刷多了leetcode就知道,斐波那契和青蛙上台阶是一个问题。
然后看了一篇博客,说动态规划最好打印出dp数组。拿斐波那契做一个例子

斐波那契

在这里插入图片描述
教科书级别的题目,f(n)=f(n-1)+f(n-2)

双层台阶

一般的台阶有两步。
在这里插入图片描述
因为上n层台阶时候,你可以上一步,也可以上两步达到n,所以取决于n-1和n-2的方法,这就是个斐波那契。

三步台阶

台阶还可以像这样上三步。
在这里插入图片描述
这里只放最通用的解法,状态之和前面三个状态有关,可以做进一步优化,这里不做优化
在这里插入图片描述

花费的台阶

在这里插入图片描述
这道题的题目讲的和屎一样。。。甚至有如下评论:
在这里插入图片描述
嗯。。相当文明,其实这个题目确实没有说清楚。。。因为假设有三个收费的楼梯[1,1,2],我们要去的地方其实是这样的,也就是说,终点其实比数组多了一格
在这里插入图片描述
事实上也好定义dp,dp【i】就是到第i个台阶时候花费的最小钱数。
dp[i] = min(dp[i-1],dp[i-2])咯,

我一开始写了这种乱七八糟的代码

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        //dp=min[dp[(n-1)+dp(n-2)]]  dp[i]表示:走到第i层阶梯的时候耗费的最小体力值
        if(cost.length==1) return cost[0];
        if(cost.length==2) return cost[1];
        if(cost.length==3) return Math.min(cost[1] + cost[2],cost[0] + cost[2]);
        int[] dp = new int[cost.length+1];
        dp[0] = cost[0] ;
        dp[1] = cost[1];
        dp[2] = Math.min(cost[1] + cost[2],cost[0] + cost[2]);
        for(int i=3;i<cost.length;i++){
            System.out.print("比较 "+dp[i-1]+"+"+cost[i]+" 与 "+ dp[i-2]+" + "+cost[i]);
            dp[i] = Math.min(dp[i-1]+cost[i], dp[i-2]+cost[i]);
        }
        dp[cost.length] = Math.min(dp[cost.length-1], dp[cost.length-2]);
        System.out.println(" ");
        for(int i:dp){
            System.out.print(" "+i);
        }
        return dp[cost.length];
    }
}

事实上推导出来以后哪里用得着这样啊
dp【i】=min(dp【i-1】,dp【i-2】),dp的数组长度比cost多一个,那么事实上初始化到dp[0]、dp[1]这两个就够了,dp【2】完全可以通过公式推导,优化一下代码

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        //dp=min[dp[(n-1)+dp(n-2)]]  dp[i]表示:走到第i层阶梯的时候耗费的最小体力值
        if(cost.length<0) return 0; 
        if(cost.length<3) return cost[cost.length-1];
        int[] dp = new int[cost.length+1];
        dp[0] = cost[0] ;
        dp[1] = cost[1];
        for(int i=2;i<=cost.length;i++){
            int tmp = i==cost.length?0:cost[i];
            dp[i] = Math.min(dp[i-1], dp[i-2]) + tmp;
        }
        return dp[cost.length];
    }
}

嗯,这样就简洁很多,当然,也可以进一步优化空间,这里不说那么细

矩阵路径

不同路径

在这里插入图片描述
这道题其实很好解答,首先机器人只能往右边或者往下边,那么在某一个点只和它左边和上边的那两个状态有关,如下图,绿色的点只能是往下走一步或者往右走一步得到。 dp[i][j] = dp[i-1][j] + dp[i][j-1];
在这里插入图片描述
那么考虑边界条件,两个选择单独做就是边界。。一直往右边或者一直往下边都是1。
在这里插入图片描述
然后看到矩阵,两个for循环写起来。。
for(int i=0;i<n;i++)
for(int j=0;i<m;j++)

最后的代码就是这样的:

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        for(int i=0;i<n;i++){
            dp[0][i] = 1;
         }
         for(int i=0;i<m;i++){
             dp[i][0] = 1;
         }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
}

这道题目不难。

带障碍的不同路径

在这里插入图片描述
然后
在这里插入图片描述
那么还是同样的思路,定义dp数组开始
dp[m][n]表示到达坐标(m,n)这一格的时候方法的总数。
然后初始化这两排。要注意如果有石头那么石头后面的都是0
在这里插入图片描述

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        if(obstacleGrid[0][0]==1){
            return 0;
        }
        int[][] dp = new int[m][n];//定义为到达i,j这个点的方法数,如果为0就是到不了
        dp[0][0] = 1;
        //dp[0][1] = obstacleGrid[0][1] == 1?0:1;
        //dp[1][0] = obstacleGrid[1][0] == 1?0:1;
        //初始化两排
        for(int i=1;i<m;i++){
            if(obstacleGrid[i][0] ==1){
                dp[i][0] = 0;
            }else{
                dp[i][0] = dp[i-1][0];
            }
        }
         for(int i=1;i<n;i++){
            if(obstacleGrid[0][i] ==1){
                dp[0][i] = 0;
            }else{
                dp[0][i] = dp[0][i-1];
            }
        }
        //开始dp
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                if(obstacleGrid[i][j]==1){
                    dp[i][j] = 0;
                    System.out.println("dp["+i+"]["+j+"]:0");
                }else{
                    dp[i][j] = dp[i-1][j] + dp[i][j-1];
                    System.out.println("dp["+i+"]["+j+"]:"+dp[i-1][j] +"+"+dp[i][j-1]);
                }
            }
        }
        //打印出dp数组
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
               System.out.print(" "+dp[i][j]);
            }
            System.out.println("");
        }
        return dp[m-1][n-1];
    }
}

矩阵路径求和

在这里插入图片描述
求和其实也跟上面同样的道理,初始化只能往右边和下边的两列
在这里插入图片描述
然后持续dp:dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1]) + grid[i][j];即可。

class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m][n];
        dp[0][0] = grid[0][0];
         //初始化
        for(int i=1;i<m;i++){
            dp[i][0] = dp[i-1][0] + grid[i][0];
        }
        for(int i=1;i<n;i++){
            dp[0][i] = dp[0][i-1] + grid[0][i];
        }
        //开始dp
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1]) + grid[i][j];
            }
        }
        //打印dp
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                System.out.print(dp[i][j]+" ");
            }
            System.out.println(" ");
        }
        return dp[m-1][n-1];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值