力扣:动态规划简单题集解

动态规划

解题固定步骤

一般,只要解决问题的阶段、状态和状态转移决策确定了,就可以写出状态转移方程(包括边界条件)。
根据动态规划的三大基本要素可以设计解题步骤如下:

  • 状态定义: 每个状态的决策,存放每个状态的变量,

  • 状态转移方程: 当前状态与上一个状态之间的关系

  • 初始状态: 初始的状态或者边界条件

    不太熟动态规划的话一定要在草稿纸上写出初始状态,然后再慢慢递推,找出状态转移方程。

    套路写多了就会了

198. 打家劫舍

198. 打家劫舍 题目链接

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

class Solution {
    public int rob(int[] nums) {
        int k=0;
        int[] dp=new int[nums.length];
        dp[0]=nums[0]; //基本条件
        if(nums.length==1)
        {
            return dp[0];
        }
        dp[1]=Math.max(nums[0],nums[1]);//基本条件
        if(nums.length==2)
        {
            return dp[1];
        }

        for(int i=2;i<nums.length;i++)
        {
            //动态转移方程
            //第一种情况:不偷当前房屋,还是前一个房屋的最大值
            //第二种情况:偷当前房屋,再加上前两个房屋的最大值
            dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]);
            									
        }
        return dp[nums.length-1];
    }
}

213. 打家劫舍 II

213. 打家劫舍 II 题目链接

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

解题思路

​ 这题比上一题多了个环型条件,为了方便起见,把环型问题转化成两个直线问题,最后比较出结果

class Solution {
    public int rob(int[] nums) {
        if(nums.length==1)
        {
            return nums[0];
        }else if(nums.length==2)
        {
            return Math.max(nums[0],nums[1]);
        }
        //把环型问题转化成两个直线问题
        int[] dp1=new int[nums.length-1]; //不包含尾
        int[] dp2=new int[nums.length];  //不包含头
        //基本条件
        dp1[0]=nums[0];
        dp1[1]=Math.max(nums[0],nums[1]);

        for(int i=2;i<nums.length-1;i++)
        {
            dp1[i]=Math.max(dp1[i-1],dp1[i-2]+nums[i]);
        }

        dp2[0]=0;
        dp2[1]=nums[1];
        dp2[2]=Math.max(nums[1],nums[2]);

        for(int i=3;i<nums.length;i++)
        {
            dp2[i]=Math.max(dp2[i-1],dp2[i-2]+nums[i]);
        }
        int m=Math.max(dp1[nums.length-2],dp2[nums.length-1]);
        return m;
    }
}

121. 买卖股票的最佳时机

121. 买卖股票的最佳时机 题目链接

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

class Solution {
    public int maxProfit(int[] prices) {
        int n=prices.length;
        int[] dp1=new int[n]; //前i天买入最小值
        int[] dp2=new int[n];  //前i天收入最大值
        //基本条件
        dp1[0]=prices[0];
        dp2[0]=0;
        for(int i=1;i<n;i++)
        {
            dp1[i]=Math.min(dp1[i-1],prices[i]);
            dp2[i]=Math.max(dp2[i-1],prices[i]-dp1[i-1]);
        }
        return dp2[n-1];
    }
}

746. 使用最小花费爬楼梯

746. 使用最小花费爬楼梯 题目链接

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

示例 1:

img

输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。

解题思路

题目说可以从下标0或者下标1开始,为了方便起见,我用了两个dp数组,dp[]从楼梯0开始,dp1[]从楼梯1开始。

最后比较两种哪个花费小,得出结果

class Solution {
    public int minCostClimbingStairs(int[] cost) {
          int n=cost.length+1;//+1的原因是,题目就要求走到楼梯顶部,而不是最后一个楼梯
          int[] dp=new int[n];
          //初始状态
          dp[0]=0;
          dp[1]=cost[0];
          for(int i=2;i<n;i++)
          {
              
              dp[i]=Math.min(cost[i-1]+dp[i-1],cost[i-2]+dp[i-2]);
          }
          
          int[] dp1=new int[n];
          //初始状态
          dp1[0]=0;
          dp1[1]=0;
          dp1[2]=cost[1];

          for(int i=3;i<n;i++)
          {
              dp1[i]=Math.min(cost[i-1]+dp1[i-1],cost[i-2]+dp1[i-2]);
          }
          if(dp1[n-1]>dp[n-1])
              return dp[n-1];
          else
              return dp1[n-1];
    }
}

64. 最小路径和

64. 最小路径和 题目链接

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

**说明:**每次只能向下或者向右移动一步。

解题思路

第一种情况:在起点,直接等于起点
第二种情况:在最左边,只能从上面走
第三种情况:在最上面,只能从座边走
第四种情况:没有任何边界,可以从两个方向走
class Solution {
    public 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++)
            {
                if(i==0 && j==0)//在起点
                {
                    dp[i][j]=grid[i][j];
                }else if(j==0)//在最左边
                {
                    dp[i][j]=dp[i-1][j]+grid[i][j];
                }else if(i==0)//在最上面
                {
                    dp[i][j]=dp[i][j-1]+grid[i][j];
                }else{//没有边界限制
                    dp[i][j]=Math.min(dp[i-1][j]+grid[i][j],dp[i][j-1]+grid[i][j]);
                }
            }
        }
        return dp[n-1][m-1];
    }
}

后来发现可以不用创建dp数组,因为每次加完grid之后,就不用了,每个方格的元素只加过一次,所以直接在元素组grid进行操作,具体代码如下:

class Solution {
    public int minPathSum(int[][] grid) {
        int n= grid.length;
        int m= grid[0].length;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(i==0 && j==0)//在起点
                {
                    continue;
                }else if(j==0)//在最左边
                {
                    grid[i][j]=grid[i-1][j]+grid[i][j];
                }else if(i==0)//在最上面
                {
                    grid[i][j]=grid[i][j-1]+grid[i][j];
                }else{//没有边界限制
                    grid[i][j]=Math.min(grid[i-1][j]+grid[i][j],grid[i][j-1]+grid[i][j]);
                }
            }
        }
        return grid[n-1][m-1];
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值