太不容易了,总算是学会了最基础的动态规划。
动态规划是什么不需要再缀述(每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。),
动态规划的使用场景一般包括三个特征:
- 最优子结构:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构。
- 无后效性:某一状态一旦确定,就不受这个状态的以后的决策影响。
- 有重叠子问题:子问题之间不是相互独立的,一个子问题在下一阶段的决策中可能被多次使用到。
动态规划经典题型:
- 最长递增子序列
- 最长公共子序列
- 最长公共子串
- 最大子序列和
动态规划有一个经典的思想:选或不选
即:在比较在该时刻(情况)下,比较“选”或者“不选”所产生的结果,选其中更符合情况的。
下面以leedcode上的一道题目为例:
可以理解为:最大不连续子序列求解问题
应用到这道题目上:
状态转移方程:
OPT(i) = max(vi+OPT(i-2),OPT(i-1))
其中:
- vi+OPT(i-2):表示选择当前i位置(房间)情况下可以盗窃到的金额
- OPT(i-1):表示不选当前i位置(房间)情况下可以盗窃到的金额
- vi:当前i房间的金额
关键:OPT[i]表示从下标为0的房间开始盗窃到下标为i的房间时可以盗窃到的最大金额(求解范围是0-i)
注意点:
- 如果i房间被盗,所以i-1号房间不能被盗,所以在前i个房间里面选择盗窃金额之和为vi+OPT(i-2)
- 如果i房间没有被盗,所以i-1号房间可以被盗,因此在前i-1个房间里面选择盗窃金额之和为OPT(i-1)
- 不向后考虑:即到第i号房间时,只考虑前面i-1房间能否被盗,而不考虑i+1房间能否被盗
代码:
/*
* 递归,非记忆化搜索
*/
// 到i房间,能够偷窃到的最高金额
var recOPT = function(arr,i){
if(i == 0)
return arr[i];
else if(i == 1)
return Math.max(arr[0],arr[1]);
else{
var a = recOPT(arr, i-2) + arr[i];//选当前i位置
var b = recOPT(arr,i-1);//不选当前i位置
return Math.max(a,b);
}
}
var rob = function(nums) {
var len = nums.length;
if(nums.length == 0)
return 0;
else{
return recOPT(nums,len-1);
}
};
/*
*非递归方式,记忆化搜索
*/
var dpOPT = function(arr){
var len = arr.length;
var opt = new Array(len);
opt[0] = arr[0];
opt[1] = Math.max(arr[0],arr[1]);
for(var i=2;i<len;i++){
var a = opt[i-2]+arr[i];//选该位置
var b = opt[i-1];//不选该位置
opt[i] = Math.max(a,b);
}
return opt[arr.length-1];
}
var rob = function(nums) {
var len = nums.length;
if(nums.length == 0)
return 0;
else{
return dpOPT(nums);
}
};