序言:
在学习使用动态规划的过程中一直没有学习到其精髓,直到我在leetcode上刷到一个题目,在寻求解法的时发现了一篇文章(https://zhuanlan.zhihu.com/p/365698607 ),我觉得对我很有帮助,于是就对学到的知识进行了摘录与梳理。
什么是动态规划?
动态规划(英语:Dynamic programming,简称 DP),是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。
简单来说,动态规划其实就是,给定一个问题,我们把它拆成一个个子问题,直到子问题可以直接解决。然后呢,把子问题答案保存起来,以减少重复计算。再根据子问题答案反推,得出原问题解的一种方法。
动态规划与递归的相同点是把它拆成一个个子问题,但是不同的是它会将子问题的结果保存起来,避免重复计算,以此来提升效率。递归是自顶向下,而动态规划是自底向上。
动态规划的核心思想
拆分子问题,记住过往,减少重复计算。
了解动态规划的简单例子
问题:假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
问题分析:
-
要想跳到第10级台阶,要么是先跳到第9级,然后再跳1级台阶上去;要么是先跳到第8级,然后一次迈2级台阶上去。
-
同理,要想跳到第9级台阶,要么是先跳到第8级,然后再跳1级台阶上去;要么是先跳到第7级,然后一次迈2级台阶上去。
-
要想跳到第8级台阶,要么是先跳到第7级,然后再跳1级台阶上去;要么是先跳到第6级,然后一次迈2级台阶上去。
通用公式
//递归解法
public int climbStairs(int n) {
return fun(n);
}
public static int fun(int n){
if(n == 1){
return 1;
}else if(n == 2){
return 2;
}
return fun(n-1)+fun(n-2);
}
//动态规划解法
public int climbStairs(int n) {
//数组用于存储子问题的结果
int[] array = new int[n+1];
//计算子问题,通过子问题的结果得出问题的结果
for(int i = 1;i <=n;i ++){
if(i == 1){//为1的特殊情况
array[i] = 1;
}else if(i == 2){//为2的特殊情况
array[i] = 2;
}else{//楼顶楼梯数大于2时,可通过子问题的结果计算得出我们想要的结果
array[i] = array[i-1] + array[i-2];
}
}
return array[n];
}
结果:
递归算法在leetcode上提交超时
而动态规划算法在leetcode上顺利通过
分析:
递归算法由于计算了子问题,通过分析代码可得时间复杂度为O(2^n)
而动态规划算法,只遍历了一次,所以时间复杂度为O(n)
从中可以动态规划的效率是很高的。
总结:
-
穷举分析:通过穷举找出规律
-
确定边界:问题不可能一直被拆分,一定有最底层的子问题可以直接得出结果。
-
找出规律,确定最优子结构:即确保子问题最优,这样我们之后通过子问题计算出的结果才会最优。
-
写出状态转移方程:即将所找到的规律转换为代码