算法最优解 算法最优解
首页
目录
赞助
GitHub (opens new window)
首页
目录
赞助
GitHub (opens new window)
  • 数据结构基础

  • 数组

  • 位运算

  • 动态规划

    • 爬楼梯
    • 零钱兑换
    • 最长递增子序列
    • 最长公共子序列
    • 单词拆分
    • 组合总和
    • 打家劫舍
      • 题目描述
      • 知识回顾
      • 思路解析
      • C++ 代码
      • 复杂度分析
    • 打家劫舍 II
    • 解码方法
    • 不同路径
    • 跳跃游戏
  • 图

  • 区间

  • 链表

  • 矩阵

  • 字符串

  • 树

  • 堆

  • 逻辑思维

  • 目录
  • 动态规划
华南溜达虎
2024-07-08
目录

打家劫舍

题目链接: https://leetcode.cn/problems/house-robber/

# LeetCode 198. 打家劫舍

# 题目描述

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

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

举个例子:

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

# 知识回顾

动态规划是一种通过将原问题分解为子问题来求解复杂问题的算法思想。它通常用于求解最优化问题,例如最长公共子序列、背包问题等。动态规划的核心思想是将原问题分解为若干个子问题,通过求解子问题的最优解推导出原问题的最优解。可以通过两点来判断一个问题能不能通过动态规划来解,一是该问题是否存在递归结构,二是对应的子问题能否记忆化。动态规划可以通过带备忘录的自上而下的递归和自下而上的迭代来分别实现。由于递归需要用到栈来实现,一些语言对递归的深度是有限制的,所以自下而上的迭代是动态规划的最佳实现方式。

# 思路解析

本题是一道经典的动态规划问题,要找到解决动态规划问题的两个突破点:推导出状态转移公式和边界条件处理。

首先定义dp[n]表示总共有n间房所能偷到的最高金额。

对于nums=[1, 2, 3, 1]其所有可能的偷盗路线如下:

根节点到叶子节点就是一条偷盗路线,每个节点表示偷到的总金额,对于上面的例子dp[4] = 4。

状态转移公式需要分两种情况讨论:

  • 偷盗路线包含第4间房(nums[3]),根据规则这个时候第3间房(nums[2])一定是没有被偷的,那么前2间房偷到的最大金额加上第4间房偷到的金额有可能是前4间房偷的最大金额dp[4] = dp[2] + nums[3]。
  • 偷盗路线不包含第4间房(nums[3]),这个时候前3间房偷到的最大金额有可能也是前4间房偷的最大金额dp[4] = dp[3]。

上面两种情况选取最大值就可以得到4间房偷到的最大金额dp[4]=max(dp[2] + nums[3], dp[3]);

扩展到一般情况dp[n]可以分解为dp[n-1],dp[n-2]两个子问题的组合,得到状态转移公式:

dp[n] = max{dp[n-2] + nums[n-1], dp[n-1]}

其中dp[n-2] + nums[n-1]表示偷第n间房整条路线可以偷到的最大金额。 dp[n-1]表示不偷第n间房整条路线可以偷到的最大金额。

对于边界条件 dp[0] = 0,dp[1] = nums[0],dp[2] = max{nums[0], nums[1]}。

# C++ 代码

class Solution {
public:
    int rob(vector<int>& nums) {
        int nums_len = nums.size();
        vector<int> dp(nums_len + 1, 0);
        if (nums_len > 0) {
            //边界条件
            dp[1] = nums[0];
        }
        if (nums_len > 1) {
            //边界条件
            dp[2] = max(nums[0], nums[1]);
        }
        for (int i = 3; i < nums_len + 1; ++i) {
            //状态转移公式
            dp[i] = max(dp[i-1], dp[i-2] + nums[i-1]);
        }
        return dp[nums_len];
    }
};

# 复杂度分析

时间复杂度: 只需要遍历一遍数组nums,所以时间复杂度为O(n),n为nums的长度。

空间复杂度: 需要借助一个dp数组,空间复杂度为O(n),n为nums的长度。

上次更新: 2024/07/28, 17:12:00
组合总和
打家劫舍 II

← 组合总和 打家劫舍 II→

Theme by Vdoing | Copyright © 2024-2024 华南溜达虎 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式