LeetCode 算法:打家劫舍 c++

题目

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

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

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

动态规划

  • 动态规划(Dynamic Programming,简称DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学等领域中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。

  • 动态规划算法通常用于解决具有重叠子问题和最优子结构性质的问题。这里的“重叠子问题”是指在递归算法中反复出现的问题,而“最优子结构”是指问题的最优解包含其子问题的最优解。

  • 动态规划的关键步骤:

    1. 识别子问题:将问题分解为小的子问题,这些子问题往往是原问题的规模较小的版本。

    2. 确定状态:定义状态(通常是一个或多个变量),这些状态能够充分描述问题的一个阶段。

    3. 状态转移方程:找到一个或多个状态转移方程,描述状态之间的关系,即当前状态是如何由前一个或多个状态推导出来的。

    4. 确定边界条件(初始条件):确定状态转移的起始点,即基本情况。

    5. 计算顺序:确定计算状态的顺序,以确保在计算当前状态之前,所需的所有子状态都已经被计算过。

    6. 构造最优解:根据子问题的解构造原问题的解。

  • 动态规划的类型:

    • 自顶向下的递归方法(Memoization):从原问题开始,逐步分解为子问题,使用记忆化(存储已解决的子问题的结果)来避免重复计算。

    • 自底向上的迭代方法:从最小的子问题开始,逐步构建解决方案,直到原问题。

  • 动态规划应用的示例:

    • 打家劫舍问题(House Robber)
    • 背包问题(Knapsack Problem)
    • 最长公共子序列(Longest Common Subsequence)
    • 最长递增子序列(Longest Increasing Subsequence)
    • 矩阵链乘问题(Matrix Chain Multiplication)
  • 动态规划是一种非常强大的算法设计技术,可以解决许多看似困难的问题。然而,它也需要对问题有深入的理解,以正确地识别子问题、状态和状态转移方程。

题解

  1. 解题思路:

“打家劫舍” 是 LeetCode 上的一道中等难度的算法题,题目编号为198。这个问题是一个典型的动态规划问题。下面我将提供这个问题的解题思路:

  • 问题描述

    • 你是一个专业的强盗,计划沿街打劫房屋。每间房子都固定金钱数量,但是相邻的房子有防盗系统,如果两间相邻的房子在同一晚上被强盗闯入,系统会自动报警。
  • 动态规划思路

    • 定义状态:定义 dp[i] 表示在第 i 家时能抢到的最大金额。
    • 状态转移方程:由于不能连续抢劫相邻的房子,所以 dp[i] 可以由 dp[i-1](不抢第 i 家)和 nums[i] + dp[i-2](抢第 i 家,但不能抢第 i-1 家)中较大者得到。
    • 初始化:dp[0] = nums[0](只抢第一家),dp[1] = max(nums[0], nums[1])(考虑只抢第一家或第二家)。
    • 遍历计算:从第三家开始遍历到第 n 家,使用状态转移方程更新 dp[i]。
    • 返回结果:最后 dp[n] 就是能抢到的最大金额。
  1. c++ demo:
#include <iostream>
#include <vector>
#include <algorithm>

class Solution {
public:
    int rob(std::vector<int>& nums) {
        int n = nums.size();
        if (n == 0) return 0;
        if (n == 1) return nums[0];
        std::vector<int> dp(n, 0);
        dp[0] = nums[0];
        dp[1] = std::max(nums[0], nums[1]);

        for (int i = 2; i < n; ++i) {
            dp[i] = std::max(dp[i - 1], dp[i - 2] + nums[i]);
        }
        return dp.back();
    }
};

int main() {
    Solution solution;
    // 测试用例1
    std::vector<int> nums1 = { 1, 2, 3, 1 };
    std::cout << "Test case 1: " << solution.rob(nums1) << std::endl; // 4

    // 测试用例2
    std::vector<int> nums2 = { 2, 7, 9, 3, 1 };
    std::cout << "Test case 2: " << solution.rob(nums2) << std::endl; // 12

    // 测试用例3
    std::vector<int> nums3 = { 1, 1, 1, 1, 1, 1, 1 };
    std::cout << "Test case 3: " << solution.rob(nums3) << std::endl; // 4

    return 0;
}
  • 输出结果:

Test case 1: 4
Test case 2: 12
Test case 3: 4

  1. 代码仓库地址:rob
  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Codec Conductor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值