稀碎从零算法笔记Day30-LeetCode:爬楼梯&打家劫舍

两道入门级别的一维DP问题,与昨天的单词划分应该放在一天的

题型:DP、子问题处理

链接:70. 爬楼梯 - 力扣(LeetCode)  198. 打家劫舍 - 力扣(LeetCode)

来源:LeetCode

爬楼梯

题目描述

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

题目样例

示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶

示例 2:

输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
提示:
  • 1 <= n <= 45

题目思路

一维dp入门问题,重点是对dp数组要理解

大问题是爬 n 楼,有几种方法,那么可以用dp[n]表示爬 n 楼用到的方法数

然后开始处理子问题:i 从 0 到 n ,处理dp[i].
怎么才能爬到 i 层?看题目描述,可以是dp[i-1]的情况下爬1层 或者是 dp[i-2]的情况下爬2层
那么可以得到:dp[i] = dp[i-1] + dp[i-2] 的递归式

然后确定数组元素的初始化:因为要遍历到dp[n],所以dp.size() 是n+1 。dp[0]按照前面定义的dp数组的含义,就相当于“爬0层”的方法——那么定义为dp[0] = 1即可。dp[1] 只能通过dp[0]到达,那么dp[1] = dp[0]。

至此,初始化完毕

C++代码

class Solution {
public:
    int climbStairs(int n) {
        // n表示楼顶
        // 类似斐波那契的一道题
        int dp[n+1];//需要遍历到dp[n]
        dp[0] = 1,dp[1]=1;
        // 爬楼梯的情况:①一次爬 1 层 ② 一次爬 2 层i
        // 即 dp[i] = dp[i-1] + dp[i-2]
        for(int i=2;i<n+1;i++)
        {
            dp[i] = dp[i-1] +dp[i-2];
        }
        return dp[n];
    }
};

结算页面


打家劫舍

题目描述

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

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

题目样例

示例 1:

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400

题目思路

这边先说一下为何这道题dp数组的容量是n:与爬楼梯对比一下,爬楼梯问题的dp数组是‘爬 k 层用到的方法’,最后要返回的是dp[n] 即爬n层会用到的方法;“小偷问题”dp[k]是“在index为k的家门口,能到手的最多的钱。”门牌号(姑且这样叫)和dp数组的index相同就不需要建立新的指针来遍历dp数组。(创dp[n+1]也可以,但没必要)

“小偷问题”——贪心味道的dp(但dp的题目好多是限制了k条件,求max / min)

那么开始按部就班:创建dp数组dp[n]: 其中dp[n-1] 表示“到了n-1 号家门口,手里最多能有多少钱”

n数字太大了,那么开始划分子问题:让 k 从 0 到 n,看一下dp[k]
根据题意,dp[k]是max(情况①,情况②)。这边笔者借用一下题解的图片

nettee 图解小偷问题icon-default.png?t=N7T8https://leetcode.cn/problems/house-robber/solutions/138131/dong-tai-gui-hua-jie-ti-si-bu-zou-xiang-jie-cjavap/

由于题目设定要求,你不能把k间房子全给偷了。
配合图片,那么两种小偷选择: ①不偷第k间房子,那么dp[k] = dp[k-1] ; ②决定偷第k间房子,那么为了保证第k间房子不会报警,第 k-1房子就不会偷,那么dp[k] = dp[k-2] + money[k]

这里相当于是把第k间房子之前的房子,偷到的钱给【黑盒化】了(即不必深究dp[i-1]是怎么偷的,因为递归会给你答案)

那么dp[i] = max(dp[i-1],dp[i-2]+money[k])

然后就是数组的初始化:dp[0]表示偷0号,那dp[0] = money[0]即可

dp[1] 的话就要看 0号 和 1号谁更有钱了,那么dp[1]= max(money[0],money[1])

从i = 2 开始遍历,初始化完成。

C++代码

class Solution {
public:
    int rob(vector<int>& nums) {
    if(nums.size() == 1)
        return *nums.begin();//类似指针:获取nums的begin迭代器后,通过 * 运算符来解引用
    // 老实dp        
    // dp[i] :到了i号门口  手头的钱(偷i个屋子能偷到的钱)
    // 状态:① 没偷钱:dp[i] = dp[i-1]  ②偷了钱:dp[i-2] + nums[i](要偷钱的话,前面的房子说明没偷)
    vector<int> dp(nums.size(),-1);//房子也是从0- n-1
    dp[0] = nums[0];
    dp[1] = max(nums[0],nums[1]);
    int i = 0,len = nums.size();
    for(int i=2;i<len;i++)
    {
        dp[i] = max(dp[i-1],dp[i-2] + nums[i]);
    }
    return dp[len-1];
    }
};

结算页面

  • 31
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值