最近作了一些leetcode的动态规划的算法题,原本我一个小小菜鸡是不配来写这个东西的,可是也壮着胆子来写一篇本身对于动态规划的理解和作题的思路,望各路大佬留情。算法
引用百度百科的一句话:数组
动态规划算法一般用于求解具备某种最优性质的问题。在这类问题中,可能会有许多可行解。每个解都对应于一个值,咱们但愿找到具备最优值的解。markdown
动态规划是一种思想,与分治思想很相似
网络
分治
问题的核心思想是:把一个问题分解为相互独立的子问题,逐个解决子问题后,再组合子问题的答案,就获得了问题的最终解。函数
动态规划的思想和“分治”有点类似。不一样之处在于,“分治”思想中,各个子问题之间是独立的:而动态规划划分出的子问题,每每是相互依赖、相互影响的。优化
引用修言大佬的一句话ui
- 最优子结构 2.重叠子问题
最优子结构
它指的是问题的最优解包含着子问题的最优解——无论前面的决策如何,此后的状态必须是基于当前状态(由上次决策产生)的最优决策。而重叠子问题
,它指的是在递归的过程当中,出现了反复计算的状况。this
首先上一道leetcode简单题:303. 区域和检索 - 数组不可变spa
示例:prototype
输入:
["NumArray", "sumRange", "sumRange", "sumRange"]
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
输出:
[null, 1, -1, -3]
解释:
NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
numArray.sumRange(0, 2); // return 1 ((-2) + 0 + 3)
numArray.sumRange(2, 5); // return -1 (3 + (-5) + 2 + (-1))
numArray.sumRange(0, 5); // return -3 ((-2) + 0 + 3 + (-5) + 2 + (-1))
来源:力扣(LeetCode)
连接:https://leetcode-cn.com/problems/range-sum-query-immutable
著做权归领扣网络全部。商业转载请联系官方受权,非商业转载请注明出处。
复制代码
这题为何定义为简单呢,你们能够想一下若是这题用暴力解法,那么咱们很容易获得结论
var NumArray = function (nums) {
this.nums = nums
};
NumArray.prototype.sumRange = function (i, j) {
const arr = this.nums.slice(i, j + 1)
return arr.reduce((a, c) => a + c)
};
复制代码
我给你们跑一下运行结果:
结果非常惨淡,执行用时1064ms,内存消耗47.1MB。为何呢?由示例咱们能够看到,sumRange
的这个函数是会屡次调用的,而咱们在里面评分的切割数组,而且使用reduce去求和,这个消耗无疑是很大的。
那还有什么解决方法呢? 动态规划能够吗?能够!
思路:为了不上面的屡次调用的状况,咱们能够把该作的操做在new
的时候一步到位,在调用sumRange
的函数的直接返回就行。
那到底该怎么用动态规划来优化呢?
个人思路是,在new这个函数的时候在函数内部维护一个dp数组
,dp数组
内存储传入数组nums
的当前项nums[i]
与i以前全部项的和。这样咱们在调用sumRange
函数时只须要返回dp[j]-dp[i]
new函数代码以下:
var NumArray = function (nums) {
this.dp = []
const dp = this.dp
dp[0] = nums[0]
for (let i = 1; i < nums.length; i++) {
dp[i] = dp[i - 1] + nums[i]
}
};
复制代码
此时咱们就维护好了dp数组
,接下来就很简单了
NumArray.prototype.sumRange = function (i, j) {
const dp = this.dp;
if (i === 0) return dp[j]
return dp[j] - dp[i - 1]
};
复制代码
为何要加一个i===0
的判断?
由于咱们在i=0的时候,就至关于拿前j项的和,而且此时时没有i-1项的。当i>1时,数学你们都学的比我好,很容易得出dp[j] - dp[i - 1]
咱们来跑下结果
完美!
第二题:leetcode中等题:198. 打家劫舍
题目:
示例:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,而后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
来源:力扣(LeetCode)
连接:https://leetcode-cn.com/problems/house-robber
著做权归领扣网络全部。商业转载请联系官方受权,非商业转载请注明出处。
复制代码
这题目简单的理解就是,规则是不能取数组相邻项,返回规则下的最大值
这题咱们主要考虑两种状况(对于第i家):
对于示例的数组,咱们画图分析: 对于第一家,第一家比较穷,只有1块钱,全部咱们第一家偷到的最多钱就是1块钱,对应的脑图和dp数组为:
第二家,中层领导,有2块钱,此时咱们有两种方案:
此时咱们能偷到的最多钱为2
显然 偷第二家比较划算 此时咱们dp[1]=2
;
此时,偷到第二家能偷到的最多钱咱们很容易获得Math.max(dp[0],dp[1])
也就是等于dp[1]
,也就是2块钱
对于第三家:高层领导,有3块钱,咱们依然有两种方案:
此时咱们用Math.max(dp[1]+nums[3],dp[2])取出最大值为4,这就是咱们第三家能偷到的最多钱。
对于第四家,一样的道理,你们能够本身理一下。这里我就直接上代码了
var rob = function (nums) {
const len = nums.length
if (len === 0) return 0;
if (len === 1) return nums[0]
if (len === 2) return Math.max(...nums)
const dp = []
dp[0] = nums[0]
dp[1] = Math.max(nums[0], nums[1])
for (let i = 2; i < len; i++) {
dp[i] = Math.max((nums[i] + dp[i - 2]), dp[i - 1])
}
return dp[dp.length - 1]
};
复制代码
最后跑一下运行结果:
完美经过!
个人理解为:咱们在遇到一些题目的题解跟拆分出来的每一项小问题有关时
,能够考虑使用动态规划的解题思路,能够清晰的理清题目的逻辑,帮助咱们快速找到答案!
因为本人技术有限,文内若有错误,敬请与我联系,谢谢!