力扣打卡:198. 打家劫舍
解题思路
理解题目
给定一组数组,找到数组中,一组和最大的序列
对于数组中的每一个元素
- 对于其和其后的元素,都有一个最大的序列
注意题目中的寻找所有可能的过程中:就是回溯的过程,这个也就是动态规划和回溯的重要区别,动态规划能够消除重复的子问题
具体来看看 planA()
纯暴力递归
public int planA(int p, int[] nums){
if(p>nums.length-1) return Integer.MIN_VALUE; // 将整数的最小值返回,超出索引,表示偷窃的不可达
// 当前的已经偷窃了,那么此时应该是下一个的下一个,而不是下一个
int max = 0;
for(int i=p; i<nums.length; i++){
int sub = planA(i+2, nums); // 此时还可以变为next的next的next,依次类推
max = Math.max(max, sub);
}
return max + nums[p]; // 表示加上当前的金额
}
写暴力递归或者是写动态规划,最重要的还是找到状态转移的过程,也就是当前状态和子问题状态之间的联系
具体来说就是:怎么将子问题的结果应用到当前的状态上,也就是题目给出的一些动作或者行为
- 每次
只能往下往右走
(问题:寻找多少种路径到达finish节点 或者 怎么走能够得到最大的value
) - 每次
只能走一个台阶或者两个台阶
(问题:多少种方式可以走到楼顶 或者 最小的步数走到楼顶 或者 花费最小的体力走到楼顶) - 当前的数
等于前一个和第前二个数的和
(问题:给定一个n,求出第n个数的值,斐波那契数列) - 当前的数
至少分解成两个数
(问题:求给定一个数的最大乘积)
重点
- 写递归函数,这个函数得到的结果一般要与题目要求的一致
- 写递归函数,就一定要认为这个递归可以得到子问题的结果,不要考虑是否可以得到呢,该怎么写呢,其实递归按照函数的定义来就可以了
代码
class Solution {
public int rob(int[] nums) {
// return planA(0, nums);
int[]memo = new int[nums.length];
Arrays.fill(memo, -1);
int max = 0;
for(int i=0; i<nums.length; i++){
max = Math.max(max, planB(i, nums, memo));
}
return max;
}
// 可以用回溯来做
// 同时可以清楚的看到这个可以使用动态规划来做,因为每一个节点,都有一个最高的金额
// 分析:按照给定的数组,假设 偷窃的顺序为 从头到尾, 那么对于每一个节点都会有最高的金额
public int planA(int p, int[] nums){
if(p>nums.length-1) return Integer.MIN_VALUE; // 将整数的最小值返回,超出索引,表示偷窃的不可达
// 当前的已经偷窃了,那么此时应该是下一个的下一个,而不是下一个
int max = 0;
for(int i=p; i<nums.length; i++){
int sub = planA(i+2, nums); // 此时还可以变为next的next的next,依次类推
max = Math.max(max, sub);
}
return max + nums[p]; // 表示加上当前的金额
}
public int planB(int p, int[] nums, int[] memo){
if(p>nums.length-1) return Integer.MIN_VALUE;
if(memo[p] != -1) return memo[p];
int max = 0;
for(int i=p; i<nums.length; i++){
int sub = planB(i+2, nums, memo);
max = Math.max(max, sub);
}
memo[p] = max + nums[p];
return memo[p];
}
}