学习动态规划的一些理解--入门级

定义

具体定义

我们可以在维基百科搜索动态规划时看到如下的话:
动态规划(英语:Dynamic programming,简称DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。

理解

不难看出基本的思想就是把一个大的问题不断分解成子问题,求解子问题从而得出原问题的解。下面举个例子:
力扣上的一道简单难度题
乍一看想直接取得最后一步的答案有点抽象,我们不妨用动态规划的思想来看这道题,虽然直接取不出最后的结果,但是可以从台阶第一级第二级的开头的基础开始想,说直白就是,第一阶和第二阶到达可能性是显而易见的,到达第三阶只有从第一阶和第二阶这两种可能,从而获得了到达第三阶的可能情况的个数,同理,再由每次只可以爬1或2个台阶,所以从第二阶第三阶的情况可以推导出第四阶的可能性,…以此类推,不难得出最后的答案。
回到前面,这一题就是把一个大的问题分解成了起初的若干个小问题,各种状态有彼此之间的联系,如同堆高楼一样,得出最后结果。(觉得楼梯比较形象,理解起来比较方便)。

具体例子

1.起点位于一个 m x n 网格的左上角 ,每次只能向下或向右移动一步,最终到达网格右下角,一共有多少种可能的情况。
这一题与上面的很相近,总的来说就是从起点开始根据各方格之间的关系不断的获得新方格的路径种类,不断向前推进,从而获得最后结果,而这一切的初始–第一行和第一列的情况也是显而易见的,有了基础起点,有了彼此关系(到达一点只有从该点上方和左方两条路径,故而到达该点情况为这相邻两点之和),得出结果也并不难。
学习动态规划的一些理解--入门级_第1张图片
随手画的有点丑,哈哈哈
按照如上思路填充几个格子如下:
学习动态规划的一些理解--入门级_第2张图片

回归主题,我们看到,这回我们又把一个大的问题,拆解成了初始若干个小问题,然后汇总(堆砌)成为最后的答案。成功扣题!

下面给出实现的代码:

    public int uniquePaths(int m, int n) {
        int[][] ans=new int[m][n];
        for(int i=0;i<m;i++){
            ans[i][0]=1;
        }
        for(int j=0;j<n;j++){
            ans[0][j]=1;
        }//动态规划开始的基础,所谓的“基石”
        for(int x=1;x<m;x++){
            for(int y=1;y<n;y++){
                ans[x][y]=ans[x-1][y]+ans[x][y-1];//各个方格之间的关系,层层递进
            }
        }
        return ans[m-1][n-1];//终点
    }

下面看一个难度略大的
2.给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。仅允许插入,删除,替换三种操作。
比如date到day的变化,可以删除e再用t替换为y,这题的意思就是这样。我们用horse和ros举例解释,首先起于“基石”,从空白格思考,从空白格到每个单词变化需要几步,我们就可以建立初步图,如下:
学习动态规划的一些理解--入门级_第3张图片
有了第一步基础,接下来就是找彼此之间的联系,通过联系初步填完接下来的方格。而每个方格的含义是将该行截至的单词转换为该列截至的单词所需的步骤数,说直白点,例如二行二列意味着将:“h”转换成“r”所需的步骤数,下面来找关系:(下面方格用数组代替)
1.如果word1.charAt(i)==word2.charAt(j),那么ans[i][j]==ans[i-1][j-1],例如例子中的三行三列,“ho”转换为“ro”所需步骤和“h”转换为“r”所需步骤是一样的。
2.ans[i-1][j]是ans[i][j]的情景下再删除当前word1的结果。
ans[i][j]是ans[i][j-1]的情景下word2再插入的结果。
ans[i][j]是ans[i-1][j-1]的情景下再替换的结果。
关系找好了,代码也就很容易出来了

    public int minDistance(String word1, String word2) {
        int m=word1.length();
        int n=word2.length();
        int[][] dp=new int[m+1][n+1];
        dp[0][0]=0;
        for(int i=1;i<m+1;i++){
            dp[i][0]=dp[i-1][0]+1;
        }
        for(int i=1;i<n+1;i++){
            dp[0][i]=dp[0][i-1]+1;
        }//建立初始的情况作为基础
        for(int i=1;i<m+1;i++){
            for(int j=1;j<n+1;j++){
                if(word1.charAt(i-1)==word2.charAt(j-1)){
                    dp[i][j]=dp[i-1][j-1];
                }
                else 
                dp[i][j]=Math.min(Math.min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1])+1;
            }
        }//逐步求解若干个小方格(化解成的小问题)
        return dp[m][n];//结果
    }

不难看出,层层推进,聚沙成塔,解决这类问题不是很难,困难还是找到彼此之间的联系,我看到的类似的题目也是在这方面,寻找联系方面较为抽象。

补充

1.由于最后结果是最优解,所以每个子问题也需要寻找该情景下的最优解
2.由于需要存储每一种状态下的数值,所以于是会有较高的空间复杂度

总结

在这方面我也是一个初学者,所以有些问题可能说的也不是很清楚,举的例子也是比较常见的问题,网上有很多解法,我也是学习了这些解法后有的自己的一些理解,所以可能说的不太好。
总结起来就是拆分问题,寻找第一步起始的基点,然后寻找子问题彼此间的联系,最后推进到终点,四步,game over!

原创文章,如有问题,欢迎指正。

你可能感兴趣的:(算法,数据结构,java,动态规划)