欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > 每日一题——打家劫舍

每日一题——打家劫舍

2025/2/22 8:50:58 来源:https://blog.csdn.net/zxjiaya/article/details/145771248  浏览:    关键词:每日一题——打家劫舍

打家劫舍(一)与打家劫舍(二)动态规划解法详解

    • 打家劫舍(一)
      • 问题描述
      • 示例
      • 解题思路
        • 动态规划
      • 代码实现
      • 复杂度分析
    • 打家劫舍(二)
      • 问题描述
      • 示例
      • 解题思路
        • 环形问题的拆分
      • 代码实现
      • 复杂度分析
    • 总结

打家劫舍(一)

问题描述

你是一个经验丰富的小偷,准备偷沿街的一排房间,每个房间都存有一定的现金。不能偷相邻的两家。给定一个整数数组 nums,数组中的元素表示每个房间存有的现金数额,计算在不被发现的前提下最多的偷窃金额。

数据范围
数组长度满足 (1 \leq n \leq 2 \times 10^5),数组中每个值满足 (1 \leq \text{nums}[i] \leq 5000)。


示例

输入输出说明
[1, 2, 3, 4]6偷第 2、4 个房间
[1, 3, 6]7偷第 1、3 个房间
[2, 10, 5]10偷第 2 个房间

解题思路

动态规划
  1. 状态定义

    • dp[i] 表示考虑前 i 个房间时的最大偷窃金额。
  2. 状态转移方程

    • 当前房间有两种选择:
      • 不偷:继承前一个房间的最大金额(dp[i-1])。
      • :当前房间金额 + 前前一个房间的最大金额(dp[i-2] + nums[i])。
    • 因此:
      [
      dp[i] = \max(dp[i-1], \ dp[i-2] + \text{nums}[i])
      ]
  3. 边界条件

    • dp[0] = nums[0]:只有一个房间时只能偷它。
    • dp[1] = \max(nums[0], nums[1]):两个房间时偷金额较大的。

代码实现

int rob(int* nums, int numsLen) {// 如果数组长度小于等于0,说明没有房子可以偷,直接返回0if (numsLen <= 0) return 0;// 如果数组长度为1,说明只有一间房子,直接返回这间房子的金额if (numsLen == 1) return nums[0];// 如果数组长度为2,说明有两间房子,选择金额较大的那间房子if (numsLen == 2) return (nums[0] > nums[1]) ? nums[0] : nums[1];// 定义一个动态规划数组dp,dp[i]表示到第i间房子为止,小偷能偷到的最大金额int dp[numsLen];// 初始化dp数组的前两个值// 如果只有一间房子,最大金额就是这间房子的金额dp[0] = nums[0];// 如果有两间房子,最大金额是这两间房子中金额较大的那个dp[1] = (nums[0] > nums[1]) ? nums[0] : nums[1];// 遍历数组,从第三间房子开始计算for (int i = 2; i < numsLen; i++) {// 对于第i间房子,有两种选择:// 1. 不偷第i间房子,最大金额就是dp[i-1](即前一间房子的最大金额)// 2. 偷第i间房子,最大金额是dp[i-2] + nums[i](即前两间房子的最大金额加上当前房子的金额)// dp[i]的值是这两种选择中的较大值dp[i] = (dp[i - 1] > (dp[i - 2] + nums[i])) ? dp[i - 1] : (dp[i - 2] + nums[i]);}// 返回dp数组的最后一个值,即所有房子的最大金额return dp[numsLen - 1];
}

复杂度分析

  • 时间复杂度:(O(n)),遍历一次数组。
  • 空间复杂度:(O(n)),存储动态规划数组。

打家劫舍(二)

问题描述

沿湖的房间组成一个闭合的圆形,即第一个房间和最后一个房间视为相邻。给定一个长度为 n 的整数数组 nums,计算在不被发现的前提下最多的偷窃金额。

数据范围:与打家劫舍(一)相同。


示例

输入输出说明
[1, 2, 3, 4]6偷第 2、4 个房间
[1, 3, 6]6偷第 3 个房间

解题思路

环形问题的拆分

由于首尾房间相邻,问题可拆分为两种情况:

  1. 不偷第一个房间:计算区间 [1, n-1] 的最大偷窃金额。
  2. 不偷最后一个房间:计算区间 [0, n-2] 的最大偷窃金额。

最终结果为两种情况的最大值。


代码实现

// 辅助函数:计算从 start 到 end 的最大偷窃金额
int robRange(int* nums, int start, int end) {int n = end - start + 1; // 计算从 start 到 end 的区间长度if (n == 1) return nums[start]; // 如果区间内只有一间房子,直接返回这间房子的金额// 定义动态规划数组 dp,dp[i] 表示从 start 开始到第 i 间房子的最大偷窃金额int dp[n];dp[0] = nums[start]; // 初始化第一间房子的最大金额dp[1] = (nums[start] > nums[start + 1]) ? nums[start] : nums[start + 1]; // 初始化前两间房子的最大金额// 遍历从第 3 间房子到最后一间房子for (int i = 2; i < n; i++) {int current = start + i; // 当前房子在原数组中的索引// 状态转移方程:选择当前房子或不选择当前房子的最大金额dp[i] = (dp[i - 1] > (dp[i - 2] + nums[current])) ? dp[i - 1] : (dp[i - 2] + nums[current]);}// 返回从 start 到 end 的最大偷窃金额return dp[n - 1];
}
int rob(int* nums, int numsLen) {// 如果只有一间房子,直接返回这间房子的金额if (numsLen == 1) return nums[0];// 如果有两间房子,返回两间房子中金额较大的那个if (numsLen == 2) return (nums[0] > nums[1]) ? nums[0] : nums[1];// 分两种情况讨论:// 1. 不偷最后一间房子,计算从第 0 间到第 (numsLen - 2) 间房子的最大金额int case1 = robRange(nums, 0, numsLen - 2);// 2. 不偷第一间房子,计算从第 1 间到第 (numsLen - 1) 间房子的最大金额int case2 = robRange(nums, 1, numsLen - 1);// 返回两种情况中的较大值return (case1 > case2) ? case1 : case2;
}

复杂度分析

  • 时间复杂度:(O(n)),两次遍历数组。
  • 空间复杂度:(O(n)),存储动态规划数组。

总结

问题动态规划状态转移方程关键点
打家劫舍(一)dp[i] = max(dp[i-1], dp[i-2] + nums[i])线性数组,直接遍历
打家劫舍(二)拆分两种情况求最大值环形数组,避免首尾同时偷窃

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词