当前位置: 首页 > article >正文

LeetCode刷题---打家劫舍问题

顾得泉:个人主页

个人专栏:《Linux操作系统》  《C/C++》  《LeedCode刷题》

键盘敲烂,年薪百万!


一、打家劫舍

题目链接:打家劫舍

题目描述

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

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

示例 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

解法

1.状态表示:

       对于简单的线性dp,我们可以用「经验+题目要求」来定义状态表示:

            i.以某个位置为结尾,巴拉巴拉;

            ii.以某个位置为起点,巴拉巴拉。

       这里我们选择比较常用的方式,以某个位置为结尾,结合题目要求,定义一个状态表示:

            dp[i]表示:选择到i位置时,此时的最高金额。

       但是我们这个题在1位置的时候,会面临「选择」或者「不选择」两种抉择,所依赖的状态需要细分:

       f[i]表示:选择到i位置时,nums[i]必选,此时的最高金额;

       g[i]表示:选择到i位置时,nums[i]不选,此时的最高金额。

2.状态转移方程:

       因为状态表示定义了两个,因此我们的状态转移方程也要分析两个:

对于f[i]:

       如果nums[i]必选,那么我们仅需知道[i - 1]位置在不选的情况下的最高金额,然后加上nums[i]即可,因此f[i] = g[i - 1]+ nums[i]

对于g[i]:

       如果nums[j]不选,那么[i - 1]位置上选或者不选都可以。因此,我们需要知道[i- 1]位宣上选或者不选两种情况下的最长时长,因此 g[i] = max (f[i - 1],g[i- 1])

3.初始化:

       这道题的初始化比较简单,因此无需加辅助节点,仅需初始化 f[0] = nums[0],g[0] = 0即可

4.填表顺序:

       根据「状态转移方程」得「从左往右,两个表一起填」

5.返回值

       根据「状态表示」,应该返回max(f[n - 1],g[n - 1])

代码实现

class Solution {
public:
    int rob(vector<int>& nums) 
    {
        int n = nums.size();
        if(n == 0) return nums[0];
        vector<int> f(n);
        auto g = f;
        f[0] = nums[0];
        for(int i = 1; i < n; i++)
        {
            f[i] = g[i-1] + nums[i];
            g[i] = max(f[i-1], g[i-1]);
        }
        return max(f[n-1], g[n-1]);
    }
};

二、打家劫舍Ⅱ

题目链接:打家劫舍 II

题目描述

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

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

示例 1:

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

示例 2:

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

示例 3:

输入:nums = [1,2,3]
输出:3

提示:

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

解法

       这一个问题是「打家劫舍」问题的变形。

       上一个问题是一个「单排」的模式,这一个问题是一个「环形」的模式,也就是首尾是相连的。但是我们可以将「环形」问题转化为「两个单排」问题:

       a.偷第一个房屋时的最大金额×,此时不能偷最后一个房子,因此就是偷[0,n - 2]区间的房子;

       b.不偷第一个房屋时的最大金额y,此时可以偷最后一个房子,因此就是偷[1,n - 1]区间的房子;

       两种情况下的「最大值」,就是最终的结果

       因此,问题就转化成求「两次单排结果的最大值」

代码实现

class Solution {
public:
    int rob(vector<int>& nums) 
    {
        int n = nums.size();
        return max(nums[0] + rob1(nums, 2, n - 2), rob1(nums, 1, n - 1));
    }
    int rob1(vector<int>& nums, int left, int right)
    {
        if(left > right)  return 0;
        int n = nums.size();
        vector<int> f(n);
        auto g = f;
        f[left] = nums[left];
        for(int i = left + 1; i <= right; i++)
        {
            f[i] = g[i-1] + nums[i];
            g[i] = max(f[i-1], g[i-1]);
        }
        return max(f[right], g[right]);
    }
};

三、删除并获得点数

题目链接:删除并获得点数

题目描述

       给你一个整数数组 nums ,你可以对它进行一些操作。

       每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。

       开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。

示例 1:

输入:nums = [3,4,2]
输出:6
解释:
删除 4 获得 4 个点数,因此 3 也被删除。
之后,删除 2 获得 2 个点数。总共获得 6 个点数。

示例 2:

输入:nums = [2,2,3,3,3,4]
输出:9
解释:
删除 3 获得 3 个点数,接着要删除两个 2 和 4 。
之后,再次删除 3 获得 3 个点数,再次删除 3 获得 3 个点数。
总共获得 9 个点数。

提示:

  • 1 <= nums.length <= 2 * 104
  • 1 <= nums[i] <= 104

解法

       其实这道题依旧是「打家劫舍」问题的变型。我们注意到题目描述,选择×数字的时候,×-1与x+1是不能被选择的。像不像「打家劫舍」问题中,选择i位置的金额之后,就不能选择i - 1位置以及i+1位置的金额呢~

       因此,我们可以创建一个大小为10001(根据题目的数据范围)的hash 数组,将nums 数组中每一个元素×,累加到,hash 数组下标为x的位置处,然后在hash数组上来一次「打家劫舍」即可

代码实现

class Solution {
public:
    int deleteAndEarn(vector<int>& nums) 
    {
        const int n = 10001;
        int arr[n] = {0};
        for(auto x : nums) arr[x] += x;

        vector<int> f(n);
        auto g = f;

        for(int i = 1; i < n; i++)
        {
            f[i] = g[i-1] + arr[i];
            g[i] = max(f[i-1], g[i-1]);
        }
        return max(f[n-1], g[n-1]);
    }
};

结语:今日的刷题分享到这里就结束了,希望本篇文章的分享会对大家的学习带来些许帮助,如果大家有什么问题,欢迎大家在评论区留言~~~ 


http://www.kler.cn/a/157024.html

相关文章:

  • 出海攻略,如何一键保存Facebook视频素材
  • 第七部分:2. STM32之ADC实验--AD多通道(AD采集三路传感器模块实验:光敏传感器、热敏传感器、反射式传感器附赠温湿度传感器教程)
  • Docker使用docker-compose一键部署nacos、Mysql、redis
  • 计算机毕业设计Python+图神经网络考研院校推荐系统 考研分数线预测 考研推荐系统 考研爬虫 考研大数据 Hadoop 大数据毕设 机器学习 深度学习
  • 绘制3D图
  • Spring框架之策略模式 (Strategy Pattern)
  • LoadBalancer将服务暴露到外部实现负载均衡purelb-layer2模式配置介绍
  • 万宾科技可燃气体监测仪科技作用全览
  • SpringBoot自定义异常处理机制
  • GEE:使用拉普拉斯(Laplacian)算子对遥感图像进行卷积操作
  • 英语语法学习 - 每周更新学英语知识点
  • 小米秒享3--非小米电脑
  • 基于YOLOv8深度学习的安全帽目标检测系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、深度学习实战
  • python之ddddocr快速识别
  • 茄子科技张韶全:跨多云大数据平台DataCake在OceanBase的实践
  • 2024最新版软件测试八股文(文档)
  • AI浪潮下,非科班出身还有机会入行程序开发领域么?
  • jquery学习笔记
  • 12.04 二叉树中等题
  • Vue3组合式API
  • DevEco Studio将常用内容设为代码模板 通过快捷键调出
  • 网工学习8-配置 STP 协议(一)
  • 【陈老板赠书活动 - 19期】-2023年以就业为目的学习Java还有必要吗?
  • 【数组】-Lc27-移除元素(相向双指针)
  • android studio 打开flutter项目 出现 dart sdk is not configured
  • navicat premium 历史版本下载地址