leetcode 21天动态规划入门——从0到0.5【Day03】打家劫舍
写在前面
干啥不好非要学人家小偷打家劫舍,不过作为一只合格的程序猿,还是要好好看看这道关于动态规划起点的一题,这道题为你铺开了动态规划的第一条大道,话不多说,开整!
DAY3
题目一
- 打家劫舍 难度系数:**
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,
影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,
如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,
计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例
示例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
思路
打家劫舍问题作为动态规划真正意义上的开山鼻祖
让我们更加的深入了解了动态方程的确立是解决此类问题的重中之重:
老样子动态规划的宗旨就是根据小部分来推拟出全局部分
1)我们来维护一个数组 用来记录 当剽窃第n间房屋时,
此时偷到的钱。
2) dp[0] = 0 ,因为当没有房屋给你偷得时候 你就没有钱
dp[1] = nums[0] 只有一间房屋时,就只能偷盗这一间房屋
dp[2] = nums[1] + dp[0] 与 dp[1] 进行比较取得最大值
所以得到状态转化方程:
当剽窃第n间房屋时,此时偷到的钱就等于
dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i-1]);
代码实现
class Solution {
public int rob(int[] nums) {
//维护一个数组来 表示当偷窃到第n间时的最大数值
int[] dp = new int[nums.length + 1];
dp[0] = 0;
dp[1] = nums[0];
//小部分推出全局
for(int i = 2; i < nums.length+1; i++) {
//动态方程
dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i-1]);
}
//返回偷窃到有n间房屋时的数值
return dp[nums.length];
}
}
执行结果
题目二
- 打家劫舍 II 难度系数:***
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。
这个地方所有的房屋都 围成一圈 ,
这意味着第一个房屋和最后一个房屋是紧挨着的。
同时,相邻的房屋装有相互连通的防盗系统,
如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,
计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
示例
示例1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例2:
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
提示
1 <= nums.length <= 100
0 <= nums[i] <= 1000
思路
如果上面那道题你都会了,这道题就是很简单的语文题了,
唯一的区别就在于 这是一个环状楼群 ,剩下的和题目一相同 ,
说白了就是有两种情况
1)如果你选择了第一个房屋偷窃,就一定不能选择最后一间了
2)如果你没有选择第一个房屋偷窃,那么你就能偷窃最后一间房屋。
说到底
第一种的情况区间只在于 1 ~ n-1
第一种的情况区间只在于 2 ~ n
取两个值中的最大值
代码实现
class Solution {
public int rob(int[] nums) {
if (nums.length == 1)return nums[0];
if (nums.length == 2)return Math.max(nums[0],nums[1]);
//进行区间选取 是不是很简单~
return Math.max(method(Arrays.copyOfRange(nums,0,nums.length-1)),method(Arrays.copyOfRange(nums,1,nums.length)));
}
//这个就是打家劫舍Ⅰ
public int method(int []nums){
int []dp = new int [nums.length+1];
dp[0] = 0;
dp[1] = nums[0];
for (int i = 2;i< nums.length+1;i++){
dp[i] = Math.max(nums[i-1]+dp[i-2],dp[i-1]);
}
return dp[nums.length] ;
}
}
执行结果
题目三
- 删除并获得点数 难度系数:**
给你一个整数数组 nums ,你可以对它进行一些操作。
每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。
之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。
开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。
示例
示例1:
输入:nums = [3,4,2]
输出:6
解释:
删除 4 获得 4 个点数,因此 3 也被删除。
之后,删除 2 获得 2 个点数。
总共获得 6 个点数。
示例2:
输入:nums = [2,2,3,3,3,4]
输出:9
解释:
删除 3 获得 3 个点数,接着要删除两个 2 和 4 。
之后,再次删除 3 获得 3 个点数,
再次删除 3 获得 3 个点数。
总共获得 9 个点数。
提示
1 <= nums.length <= 2 * 10^4
1 <= nums[i] <= 10^4
思路
一小部分模拟全局
有了前两道题的经验
就要学会先去找状态转化方程了
这道题唯一的思考点在于需要维护一个数组map,
用来存储当前这个数的数量。
dp[i] = Math.max(dp[i-1],dp[i-2] + map[i] *i);
代码实现
class Solution {
public int deleteAndEarn(int[] nums) {
if (nums.length == 1)return nums[0];
int max = Integer.MIN_VALUE;
//获取当前数组中的最大值作为map的大小
for (int i : nums){
max = Math.max(max,i);
}
//维护一个数组map用来记录每个数值出现的次数
int[] map = new int [max + 1];
for (int i : nums){
map[i] ++ ;
}
//动态方程的模拟推导
int dp[] = new int [max + 1];
dp[1] = map[1];
dp[2] = map[2] * 2;
for (int i = 2 ;i< max+1;i++){
dp[i] = Math.max(dp[i-1],dp[i-2] + map[i] *i);
}
return dp[max];
}
}
执行结果
写在最后
今天是咱们学习动态规划入门的第三天
我相信 在各位认真做了 这三道题后
一定会对动态规划有了自己的套利
还是那句话 用一小部分模拟推导全过程
找到状态转化方程
再难的题 也会迎刃而解
加油!!!
最后
每天进步点 每天收获点
愿诸君 事业有成 学有所获
如果觉得不错 别忘啦一键三连哦~