leetcode-打家劫舍

本文详细分析了LeetCode上的打家劫舍问题,包括简单版和进阶版。作者首先尝试使用贪心策略解决,但发现此方法并不适用,然后转向动态规划的方法。对于进阶版,考虑到环形结构,作者提出从两个起点开始的策略,并给出了相应的代码实现。此外,文章还提到了另一道类似问题——涉及二叉树结构的打家劫舍,暗示了问题的复杂性和多样性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

打家劫舍分简单版和进阶版两个题目,分别对应leetcode-198以及leetcode-213,

普通版题目:专业的小偷,每间房内都藏有一定的现金,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

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

进阶版的题目加了一个条件,房子首尾相连;

思路

我是先看的进阶版题目,首尾相连的屋子,所以这次从进阶版题目开始说起。

拿到题目的时候,第一个反应是贪心(后来血泪史告诉我这个想法是错误的),想着先找到钱最多的那家开始偷,然后再比较n+2和n+3的值,哪个值大就偷哪家(就这样还让我过了二十多个样例):比如(**[1,7,9,4]**这个例子就不满足)

然后我又想啊,实际上这也就两种情况,一种是从第一家开始偷,一种是从第二家开始偷,下一次偷的时候比较一下n+2和n+3的谁大,谁大要谁,貌似逻辑比刚才那个要清晰一点点,于是出现了下面这段代码:

var rob = function(nums) {
// 第一家和最后一家只能选一家来偷
// 每偷一家可以选择它的下一家,下下一家甚至更远的那家
// 假设偷第n家的时候金额最大,第n-1家时就是除了自己之外相加最大的值
// 按照贪心,先找出最有钱那家作为第一家(因为是一个圈,所以哪家作为第一家都是可以的)
// 然后根据某一个方向开始找,每次比较n+1,n+2的值,取最大那个,直到n+1或者n+2等于最后一个数
const maxNum = Math.max(...nums);

const len = nums.length;
if(len < 4){
    return maxNum;
}
// 数组处理,将最大的那个数作为第一个数,它前面的数都丢到它后面
// const maxIndex = nums.indexOf(maxNum);
// let newArr = nums.splice(0,maxIndex);
// nums = nums.concat(newArr);

// let index = 0

const getMax=(i,arr)=>{
  
    let newArr = arr.splice(0,i);
arr = arr.concat(newArr);
    let res = arr[0];
    let index = 0;
    console.log(res)
while(index < len -1){
if(index +1 === len-1 || index +2 === len-1){
    console.log(res,index)

    return res
} 

let comparedMax = index + 3 === len-1 ? arr[index + 2] : Math.max(arr[index + 2],arr[index +3]);
console.log(res,comparedMax)
res += comparedMax
index +=index + 3 === len-1 ? 2 : (arr[index + 2]>= arr[index +3] ? 2:3)

}
}

return Math.max(getMax(0,nums),getMax(1,nums))
}

有那么点点道理的同时,其实也会有问题,比如**[2,4,8,9,9,3]**,一眼就看出来取2,8,9,但是按照贪心,第二次对比的时候,比较8和9,就会选择9,导致最后只会选择2,9,3

两次失败证明贪心在这道题中可行性不高,但回过头来看,按照我的分析,前三点是没毛病的,那我们现在来想想,初始情况就两种,要么从第一家开始偷(这个时候不能偷最后一家),要么从第二家开始偷,到最后比较的应该就是这两种情况下的最大值。

const rob=(nums)=>{
  const len = nums.length;
  const getMax=(start,end)=>{
    // TODO
  }
  getMax(0,len-1),getMax(1,len)
}

架子搭起来之后,先不急着考虑写函数内容,先看看特殊情况,因为是首尾相连,所以当房子数小于4的时候,房子都是相连的,这个时候谁家钱多就去谁家,于是有以下判断条件:

if(len < 4) return Math.max(...nums) // Math.max([]) === 0

现在该正式写两种情况怎么偷了,两种情况实际上都是一样的,假设现在想偷第i家时,怎么样性价比最高呢,两种情况,偷或者不偷,不偷意味着偷i-1家之后得到的金额大于在i-2家时的状态,因为偷了i-1家就不能偷第i家,在i-2家的状态不一定是偷了i-2家,而是在i-2家是获得的最大金额(陷入了一个重复的逻辑)。所以我们可以写出一个公式:

dp[i]=Math.max(dp[i-2]+ num[i],dp[i-1])

于是就写出了下面这段代码:

var rob = function(nums) {
// 第一家和最后一家只能选一家来偷
// 每偷一家可以选择它的下一家,下下一家甚至更远的那家
// 假设偷第n家的时候金额最大,第n-1家时就是除了自己之外相加最大的值
const len = nums.length;
if(len < 4) return Math.max(...nums);
const getMax=(start,end)=>{
	let first = 0
        let second = 0
        for (let i = start; i < end; i++) {
           let temp = second;
        second = Math.max(first + nums[i],second);
        first = temp;
        }
        return second
}
  
return Math.max(getMax(0,len-1),getMax(1,len))
}

于是,没有连成一个圈的打家劫舍也很快做出来了

var rob = function(nums) {
    const len = nums.length;
    if(len < 3) return Math.max(...nums);
    let first = nums[0];
    let second = Math.max(first, nums[1]);
    for(let i = 2; i < len;i++){
        let temp = second;
        second = Math.max(first + nums[i],second);
        first = temp;
    }
    return second
};

然而,在我准备睡觉的时候,我又发现了另外一个打家劫舍(leetcode-337)

题目:在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

这道题下回分解。。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值