本文主要是介绍阿亮的算法之路——198. 打家劫舍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
集中练习动态规划的题目。
题目描述
题目链接:https://leetcode-cn.com/problems/house-robber/
解题
看了一遍题目之后,我知道这个题目是那种当前状态需要由前面的状态来确定的题目,也就是用动态规划,但是我不知道状态怎么确定,怎么写状态转移。
本着先完成,后完美的想法。我决定先用自己的思路做一遍,不用动态规划,然后我就思考用最笨的办法来做
一往这个方向思考,就陷入了一个漩涡:会不会有两个很大的数,它们俩中间夹了两个数,导致他们俩中间的那那个数都不会取?如果要考虑,就需要考虑很多很多种情况,那么用最笨的办法,就无法列举出所有情况
这种类似的动态规划的问题,好像都很难用最笨列举做出来,我之前也遇到过一个,想死都没想出来,后来看了题解才知道需要用动态规划。但是那个题目本身有难度,而且我那时也没接触过动态规划,所以就放弃了那个题。
最笨的办法不行,我就尝试用用动态规划来思考,但是好像也没啥思路。我知道要找状态转移方程,但是我找不到这个方程。苦思无果之后,我决定去看看解题思路。
解题思路
一看,茅塞顿开、豁然开朗。其实思路也很简单,只是自己没想到。
首先考虑最简单的情况。如果只有一间房屋,则偷窃该房屋,可以偷窃到最高总金额。如果只有两间房屋,则由于两间房屋相邻,不能同时偷窃,只能偷窃其中的一间房屋,因此选择其中金额较高的房屋进行偷窃,可以偷窃到最高总金额。
如果房屋数量大于两间,应该如何计算能够偷窃到的最高总金额呢?对于第 k (k>2) 间房屋,有两个选项:
偷窃第 k 间房屋,那么就不能偷窃第 k−1 间房屋,偷窃总金额为前 k−2 间房屋的最高总金额与第 k 间房屋的金额之和。
不偷窃第 k 间房屋,偷窃总金额为前 k−1 间房屋的最高总金额。
在两个选项中选择偷窃总金额较大的选项,该选项对应的偷窃总金额即为前 k 间房屋能偷窃到的最高总金额。
用 dp[i] 表示前 ii 间房屋能偷窃到的最高总金额,那么就有如下的状态转移方程:
dp[i] = max(dp[i-2]+nums[i], dp[i-1])
边界条件为:
- dp[0]=nums[0] 只有一间房屋,则偷窃该房屋
- dp[1]=max(nums[0],nums[1]) 只有两间房屋,选择其中金额较高的房屋进行偷窃
最终的答案即为dp[n−1],其中 n 是数组的长度。
代码
看了思路之后,我觉得写代码就很轻松了,之前很难理解,主要是因为对动态规划不熟悉。
public static int rob(int[] nums){int len;if (nums == null || (len=nums.length)==0) return 0;int [] dp = new int[len];dp[0] = nums[0];if (len == 1) return dp[len-1];dp[1] = nums[0]>nums[1]?nums[0]:nums[1];if (len == 2) return dp[len-1];for (int i = 2; i < len; i++){ dp[i] = nums[i]+dp[i-2] > dp[i-1]?nums[i]+dp[i-2]: dp[i-1]; }return dp[len-1];}
其实代码也很简单,主要是上面提的动态规划的解题思路。
提交结果
一开始看到这个结果,我还以为是页面提交加载出错了。再刷多提交了基础层,才确定是真的,但是这个题的提交结果数据好像有问题,我看到评论里还有执行时间超过100%的人。
总结
做了几次动态规划的题之后,我觉得这个套路,和递归好像啊。都需要有个边界条件,这个边界条件会一步一步的影响最后的结果。动态规划需要写状态转移方程,其实递归也需要类似的操作:递归调用的时候,要注意参数。
不同的是,递归是一直在调用自己,而动态规划,是在一直往前找之前的状态。
这篇关于阿亮的算法之路——198. 打家劫舍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!