LeetCode刷题日记07【746,198,213】

本文介绍了如何运用动态规划方法解决两个编程问题:最小花费爬楼梯和打家劫舍系列(I和II),通过构建dp数组并利用递推公式找到到达目标状态的最小成本或最大收益。
摘要由CSDN通过智能技术生成

746. 使用最小花费爬楼梯

题目描述

746. 使用最小花费爬楼梯

 

解题思路

要求到达楼顶的最小花费【也就是下标为3时】


1. dp数组定义dp[i]【到达下标i的位置所需要的花费】:

下标记录到哪个台阶

数值记录最小的消耗


2. 递推公式:

等于dp[i-1]跳了一步:dp[i-1] + cost[i-1]【可以理解为到达i-1位置之前的最小总花费加上当前从i-1位置继续再往上跳的花费】

或者等于dp[i-2]跳了两步得到:dp[i-2] + cost[i-2]

因为本题要求的是求最小的花费,而题目允许两种跳法,因此等于两种的更小的那个

因此dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])


3. 初始化:

因为最初可以从位置0或1开始,所以初始化dp[0]和dp[1]即可,数值为0【因为根据dp数组定义,因此dp[0]和dp[1]代表到达下标0或1的位置所需要的花费,没有跳则没有花费,也就是0】


4. 遍历顺序:

因为递推公式是后面的元素根据前面的元素来递推【i可以由i-2或i-1得到】,所以正常从前往后遍历即可


5. 举例推导dp数组:

引用自代码随想录对应题的图辅助理解

代码

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        // 1.dp数组定义:到达下标i的位置所需要最小花费【加1是因为根据数组定义所以还需要记录一个下标为楼顶最小花费】
        int[] dp = new int[cost.length + 1];
        // 3.初始化:根据递推公式因此只需初始化0和1索引
        dp[0] = 0;
        dp[1] = 0;
        // 4.遍历顺序【0和1位置已经初始化,从2开始】
        for (int i = 2; i <= cost.length; i++) {
            // 2.递推公式:dp数组可通过向上爬一个或两个台阶得到
            dp[i] = Math.min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2]);
        }
        // 返回dp数组的最后一个元素
        return dp[cost.length];
    }
}

198.打家劫舍

题目描述

198.打家劫舍

解题思路

线性数组相邻的房间不能偷

该房子是否能偷取决于前一个和前二个房屋是否偷的情况【动态选择性的结果】


1. dp数组定义

包含下标i之前【包含下标i当前房子】偷的最大的总和


2. 递推公式【哪些步能够推导得到dp[i]】

偷当前i:dp[i-2] + num[i] 

不偷当前i:dp[i-1] 

因此dp[i] = max(dp[i-2] + num[i], dp[i-1] )


3. 初始化

i可以由i-2或i-1得到,因此需要初始化dp[0]和dp[1]

dp[0] = num[0]

dp[1] = max(num[0], num[1])


4. 遍历顺序

因为递推公式是后面的元素根据前面的元素来递推【i可以由i-2或i-1得到】,所以正常从前往后遍历即可(for (i = 2; i < num.size(); i++))


5. 举例推导dp数组

引用自代码随想录对应题的图辅助理解

代码

记得先根据题意处理数组的特殊情况确保不会出现ArrayIndexOutOfBoundsException报错

class Solution {
    public int rob(int[] nums) {
        // 根据题意1 <= nums.length <= 100说明最短数组可能只有一个元素,处理特殊情况
        if (nums.length == 1) return nums[0];
        // 1.dp数组定义:包含下标i及之前偷的最大的总和
        int[] dp = new int[nums.length];

        // 3.初始化:根据递推公式只需要初始化索引0和1位置
        dp[0] = nums[0];
        dp[1] = Math.max(dp[0], nums[1]);
        // 4.遍历顺序:从前向后遍历,从第二个开始
        for (int i = 2; i < nums.length; i++) {
            // 2.递推公式【相邻不能偷】:偷当前或不偷当前
            dp[i] = Math.max(dp[i-2] + nums[i], dp[i-1]);
        }
        // 返回dp数组的最后一个元素
        return dp[nums.length - 1];
    }
}

213.打家劫舍II

题目描述

213. 打家劫舍 II

解题思路

把环形问题展开成线性问题,然后单独考虑首尾元素情况

相邻的房间不能偷,以及首尾相连即相邻不能偷


三种情况:

1. 不考虑首尾元素,只考虑中间部分:

2. 考虑首元素,但不考虑尾元素

3. 考虑尾元素,但不考虑首元素


情况2和情况3包含了情况1因为都考虑了中间部分【考虑的部分决定了遍历的范围

因此只需要求情况2和情况3取最大值即可,情况1不需要单独再求了【因为23情况是考虑了1情况加上首元素或者尾元素偷或不偷,而偷或不偷取决于递推公式

代码

class Solution {
    public int rob(int[] nums) {
        // 根据题意1 <= nums.length <= 100说明最短数组可能只有一个元素,处理特殊情况
        if (nums.length == 1) return nums[0];
        // 和打家劫舍1区别在于首尾元素不能同时取则有三种情况,1.取中间、2.取中间和首元素、3.取中间和尾元素
        // 情况2和3包含了1,所以只需考虑情况2和3
        // 情况2:
        int result1 = robRange(nums, 0, nums.length - 2);
        // 情况3:
        int result2 = robRange(nums, 1, nums.length - 1);
        return Math.max(result1, result2);
    }
    private int robRange(int[] nums, int start, int end) {
        // 加入首尾的判断
        if (start == end) return nums[start];
        // 打家劫舍1的逻辑:
        // 1.dp数组定义:包含下标i及之前偷的最大的总和
        int[] dp = new int[nums.length];
        // 3.初始化:根据递推公式只需要初始化索引start和start+1位置
        dp[start] = nums[start];
        dp[start + 1] = Math.max(dp[start], nums[start + 1]);
        // 4.遍历顺序:从前向后遍历,从第start+2个开始
        for (int i = start + 2; i <= end; i++) {
            // 2.递推公式【相邻不能偷】:偷当前或不偷当前
            dp[i] = Math.max(dp[i-2] + nums[i], dp[i-1]);
        }
        // 返回dp数组的最后一个元素
        return dp[end];
    }
}

  • 23
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值