今天就是打家劫舍的一天,这个系列不算难,大家可以一口气拿下。
视频讲解:https://www.bilibili.com/video/BV1Te411N7SX
https://programmercarl.com/0198.%E6%89%93%E5%AE%B6%E5%8A%AB%E8%88%8D.html
直接看题解学习新算法
这道题其实不难,和爬楼梯很像,就是dp算法基本的做法:先写几个,找找规律,就做出来了
最主要的是dp数组的含义:考虑到下标为 i 的那户,能偷的最大金额是 dp[i] 元 (考虑到第3家,不代表一定偷第三家,比如最后返回的事dp[length - 1],不代表一定偷了最后一家)
注意nums如果只有一个元素的话,初始化就回数组越界了。
动笔算,不要懒
class Solution {
public int rob(int[] nums) {
if (nums.length == 1) return nums[0];
int[] dp = new int[nums.length];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for (int i = 2; i < dp.length; i++) {
dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
}
return dp[dp.length - 1];
}
}
视频讲解:https://www.bilibili.com/video/BV1oM411B7xq
https://programmercarl.com/0213.%E6%89%93%E5%AE%B6%E5%8A%AB%E8%88%8DII.html
对比起抢劫1,就是成环了。那么偷了第一家就不能偷最后一家,感觉影响初始化的。
那么我觉得就是分两种,一个是抢了第一家,就不能抢最后一家。另一个是 抢了第二家,最后一家抢不抢看情况。最后两个数组取最大的。
虽然方法比较笨,但是做对了,看看题解有没有精妙的。
和我一样,我是天才
无
真是天才
我的笨方法。
class Solution {
public int rob(int[] nums) {
if (nums.length == 1) return nums[0];
int[] dpOne = new int[nums.length];
int[] dpLast = new int[nums.length];
//抢第一家,不抢第二家,且不抢最后一家
dpOne[0] = nums[0];
dpOne[1] = dpOne[0];
//不抢第一家,抢第二家,最后一家递推
dpLast[0] = 0;
dpLast[1] = nums[1];
for (int i = 2; i < dpOne.length - 1; i++) {
dpOne[i] = Math.max(dpOne[i - 2] + nums[i], dpOne[i - 1]);
}
dpOne[dpOne.length - 1] = dpOne[dpOne.length - 2];
for (int i = 2; i < dpLast.length; i++) {
dpLast[i] = Math.max(dpLast[i - 2] + nums[i], dpLast[i - 1]);
}
return dpOne[dpOne.length - 1] > dpLast[dpLast.length - 1] ? dpOne[dpOne.length - 1] : dpLast[dpLast.length - 1];
}
}
视频讲解:https://www.bilibili.com/video/BV1H24y1Q7sY
https://programmercarl.com/0337.%E6%89%93%E5%AE%B6%E5%8A%AB%E8%88%8DIII.html
就是不能同时打劫父子节点,但也不是一层一层来。要看最大的
我试试
失败了,树形的我找不到dp数组和递推公式
悟了,这道题挺复杂的,又要dp又要递归,但我觉得核心其实是递归。
递归的思路就是对于一个节点来说,它有两个可能,偷或者不偷。
看到这,也该知道是后序遍历才对,因为要根据左右孩子的价值来算自己的。
递归的问题基本就解决了。
那么dp数组在哪呢?如果保存左孩子不偷的价值和偷的价值,就是dp数组。这个数组大小是2,dp[0] 代表不偷这个节点的最大价值,dp[1]代表偷这个节点的最大价值。
那么dp数组怎么记录每个节点的呢?把它当做递归的返回值,他在一层层递归的时候,返回给父节点用于计算。
所以递归三部曲
我自己写写试试
做对了
理解思路之后就没有实现困难了
关键是要讨论当前节点抢还是不抢,也就是所谓的状态。dp题目感觉首先要抓住状态,然后看这个状态怎么变化的,找到那个状态转移公式,也就是递推公式。
这道题目算是树形dp的入门题目
class Solution {
public int rob(TreeNode root) {
int[] dp = robTree(root);
return Math.max(dp[0], dp[1]);
}
private int[] robTree(TreeNode node) {
if (node == null) {
int[] array = {0, 0};
return array;
}
//单层递归逻辑
int[] leftdp = robTree(node.left);
int[] rightdp = robTree(node.right);
//根据左右节点算出自己
int curSteal = leftdp[0] + rightdp[0] + node.val;
int curNotSteal = Math.max(leftdp[0], leftdp[1]) + Math.max(rightdp[0], rightdp[1]);
//不偷自己dp[0]偷自己dp[1]
int[] dp = {curNotSteal, curSteal};
return dp;
}
}