目录
一、动态规划
动态规划(简称DP)的思想是把一个大的问题进行拆分,细分成一个个小的子问题,且能够从这些小的子问题的解当中推导出原问题的解。
二、性质
1、最优子结构性:既所拆分的子问题的解是最优解
2、无后效性:即子问题的解一旦确定,就不再改变,不受在这之后、包含它的更大的问题的求解决策略影响。
3、子问题重叠性质:既在求解的过程当中,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是在表格中简单地查看一下结果,从而获得较高的解题效率。
三、典型问题
1、斐波那契数列
const f = new Array()
function dp(n)
{
f[1] = 1;f[2] = 1;
for(let i = 3;i <= n ;i ++)
f[i] = f[i-1] + f[i-2];
}
console.time('time') //算法的开始时间
dp(50)
console.timeEnd('time') //算法的结束时间
let str = ""
for(let i=3;i<f.length;i++){
str += f[i]+"\t"
}
console.log(str)
//输出结果:time: 0.182ms
//2 3 5 8 13 21 34 55 89 144 //233
//377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 //75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 //9227465 14930352 24157817 39088169 63245986 102334155 //165580141 267914296 433494437 701408733 1134903170 1836311903 //2971215073 4807526976 7778742049 12586269025
四、求解的基本步骤
动态规划所处理的问题是一个多阶段决策问题,一般由初始状态开始,通过对中间阶段决策的选择,达到结束状态。这些决策形成了一个决策序列,同时确定了完成整个过程的一条活动路线(通常是求最优的活动路线)。
初始状态→│决策1│→│决策2│→…→│决策n│→结束状态
1、划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。
2、确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。
3、确定决策并写出状态转移方程:因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。但事实上常常是反过来做,根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程。
4、寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。
五、案例
1、爬梯子问题
假设你正在爬楼梯,需要 n 阶你才能到达楼顶。每次可以爬1或2阶台阶,可以有多少种不同的方法爬到楼顶
假设,我求5阶台阶有多少种上法;五级台阶可以是走一阶台阶加上四阶台阶的种类,和走二阶台阶加上三阶台阶的种类,这样就是五阶台阶的上法等于四阶台阶的上法加上三阶台阶的上法。
//爬楼梯问题
function climbStairs(n) {
if (n === 1 || n === 2) {
return n;
}
var ways = [];
ways[0] = 1;
ways[1] = 2;
for(var i=2; i<n; i++){
ways[i]=ways[i-1] + ways[i-2];
}
return ways[n-1];
}
console.log(climbStairs(3));//3
2、最大和的连续子数组
给定一个整数数组nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大值
示例:输入:[-2,1,-3,4,-1,2,1,-5,4] 输出:6
解释:连续子数组[4,-1,2,1]的和最大为6
function maxSubArray(nums) {
let dp = new Array(nums.length);
//初始化数据
dp[0] = nums[0];
let ans = nums[0];
for(let i = 1; i < nums.length; i ++){
//获取以nums[i]结尾的最大值
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
//记录最大值
if(dp[i] > ans) ans = dp[i];
}
return ans;
}