动态规划优化篇

Posted DebugDebut

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划优化篇相关的知识,希望对你有一定的参考价值。

小白学习动态规划:优化篇

上一篇讲述了动态规划入门级题目,代码都是没有优化的,如果没有看过的读者也没关系,在下面会贴出这两道题目的所有代码,包括没有优化的和优化之后的。感兴趣的读者可以先去看一下上一篇的题目,都是EASY级别的题目。
今天主要是对上一篇博客的两道题目进行优化,对于绝大多数利用动态规划的算法题作优化时,个人认为最重要的优化方法就是:画DP的图!找出状态之间的依赖关系,将其它没有依赖的状态废弃掉。

如果你不懂没有关系,可以看以下例子,很好理解~

优化类型一:一维降变量

LeetCode70. 爬楼梯

问题描述:假设你需要爬楼梯,需要爬n阶才能到达楼顶,每次可以爬1或2阶,多少种不同的方法可以爬到楼顶?

未优化前的代码:

 1class Solution {
2    public int climbStairs(int n) {
3        int[] dp = new int[n];
4        for (int i = 0; i < dp.length; i++) {
5            if(i == 0){ //第1阶
6                dp[i] = 1;
7            }else if(i == 1){ //第2阶
8                dp[i] = 2;
9            }else//第3阶及以上
10                dp[i] = dp[i-1]+dp[i-2];
11            }
12        }
13        return dp[n-1];
14    }
15}

首先,我们先分析dp的填充过程,观察有什么值是会在某个时间段后作废(一直没有使用到)

动态规划优化篇



动态规划优化篇


动态规划优化篇


动态规划优化篇


从上面几幅图可以很清晰地观察到,从求第四阶楼梯的爬楼梯方法数之后,每求下一阶楼梯的方法数时,就会有多一个值被废弃掉,而求某个状态的值(爬楼梯的方法数)只与它的第(n-2)个状态和第(n-1)个状态[n >= 3]有依赖关系,所以在第(n-2)个状态以前的值就被废弃掉了。

所以,这个一维DP数组在某种程度上是浪费了内存空间的。

如何优化?

动态规划优化篇



颜色的含义(帮助你理解整个过程):

蓝色:初始化的状态值

黄色:得出下一个状态的状态值

红色:将当前状态的第(n-1)个状态转化为第(n-2)个状态

绿色:将第n个状态转化为第(n-1)个状态

动态规划过程:

求出一个新的状态时,将当前状态的第(n-1)状态更新为第(n-2)个状态,将当前状态更新为第(n-1)个状态,继续求下一个新的状态,直到循环结束。

优化后的代码:

 1class Solution {
2    public int climbStairs(int n) {
3        if(n <= 2){return n;}
4        int dp0 = 1;
5        int dp1 = 2;
6        for(int i = 3; i <= n; i++){
7            int result = dp0 + dp1;
8            dp0 = dp1;
9            dp1 = result;
10        }
11        return dp1;
12    }
13}

优化类型二:二维降一维

LeetCode62. 不同路径

问题描述:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

问总共有多少条不同的路径?

动态规划优化篇


说明:m 和 n 的值均不超过 100。

未优化前的代码:

 1class Solution {
2    public int uniquePaths(int m, int n) {
3        int[][] dp = new int[m][n];
4        for (int i = 0; i < m; i++){
5            for (int j = 0; j < n; j++){
6                //上边界
7                if(i == 0 && j >= 0){dp[i][j] = 1;continue;}
8                //左边界
9                if(j == 0 && i >= 0){dp[i][j] = 1;continue;}
10                //其它情况
11                dp[i][j] = dp[i-1][j] + dp[i][j-1];
12            }
13        }
14        return dp[m-1][n-1];
15    }
16}

观察未优化前的DP矩阵:

动态规划优化篇



状态转移方程:
动态规划优化篇
当填充第二行时,情况是这样的:

动态规划优化篇


当填充第三行时,情况是这样的:

动态规划优化篇


细心的我们会发现:第一行数据已经没有用处了,它的存在与否不影响我们求第三行的数据。所以,我们只需要保存上一行数据,就可以得出下一行的数据,并且每求出下一行的一个数据时,都可以舍弃掉上一行的那一个数据,所以只需要一维数组保存一行数据就可以求出下一行的数据。

如果你不是很懂,那么看下面几幅图就会秒懂~

注意左边数组行下标一直为0,表示该数组本质上只有一行

动态规划优化篇


动态规划优化篇


原本二维dp数组的状态转移方程为:
动态规划优化篇
在图中从位置上表现出来的结果是正确的,但是!!!重点来了!

注意看!你会发现状态转移方程转化为:    
动态规划优化篇
最后数组就会呈现出下图得到状态:

动态规划优化篇


优化后的一维dp代码:

 1class Solution {
2    public int uniquePaths(int m, int n) {
3        if(m <= 1 || n <= 1){return 1;}
4        int[] dp = new int[n];
5        for(int i = 0; i < m; i++){
6            for(int j = 0; j < n; j++){
7                if(j == 0){dp[j] = 1;continue;}
8                dp[j] = dp[j-1] + dp[j];
9            }
10        }
11        return dp[dp.length - 1];
12    }
13}






喜欢本文吗?顺手右上角分享文章

没有关注的小伙伴们关注一波


您小小的举动就是对我们最大的支持!





DebugDebut


动态规划优化篇 

长按二维码 



 

大家想了解什么计算机基础知识也可以私信给我们,我们一定尽量满足您的需求~

再点个在看啦

以上是关于动态规划优化篇的主要内容,如果未能解决你的问题,请参考以下文章

一篇小白也能看懂的动态规划算法文章

leetcode 746. 使用最小花费爬楼梯----动态规划篇

经典动态规划:高楼扔鸡蛋(进阶篇)

基于动态规划的优化器

LeetCode解题总结动态规划篇

动态规划(普及组)