动态规划算法之路径问题-其一

70 阅读2分钟

什么是动态规划算法?

       按我自己的理解,动态规划是一种自底向上的思维,即最后一步的结果可由倒数第二步推导过来,倒数第二步可由倒数第三步推导过来,以此类推,直到推到第一步,而我们的解题思路则将顺序反过来,由第一步的结果推导到第二步,而第一步的结果一般都是已知的,即从已知结果出发,一步一步推导到最后一步。

       当然,这里的推导是需要你写出状态转移方程,什么是状态转移方程呢?简单来说就是 第n步与第n-1步的关系,有点像数学里面的抽象函数 f(n) = f(n-1)  +  x, 不过这里的x也是抽象的依赖关系。

什么问题一般可以使用动态规划算法解决?

****先放一个取巧的方法,根据数据范围判断,因为 DP 是一个递推的过程,因此如果数据范围是10^5 至10^6 的话,可以考虑是不是可以使用一维 DP 来解决;如果数据范围是10^2至10^3的话,可以考虑是不是可以使用二维 DP 来做。

**** 理解了动态规划的思想后,我们一般从要求的问题出发,看看要求的问题可不可以拆分成一步一步去处理,举个经典的路径问题:

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

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

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

        我们从问题出发,问到达最后的终点时共有多少种路径,那我们按照动态规划的思维倒推,最后终点位置的路径总数,等于 到达终点左侧位置时的路径总数 加上 到达终点上方位置时的路径总数(因为只能往右走或者往下走),而这两个位置又以此类推,所以我们可以归纳下状态转移方程

当前不处于第一行或者第一列时,f(当前位置) = f(左侧) + f(上方)

当前处于第一行时,f(当前位置) = f(左侧)

当前处于第一列时,f(当前位置)= f(上方)

       假设以一个二维数组存储上面这个网格,以r代表行数,c代表列数,则可以将上面的状态转移方程转化成表达式

当 r>0和c>0时,f(r,c) = f(r,c-1) + f(r-1,c) 

当 r = 0时,f(r,c) = f(r,c-1)

当 c = 0时,f(r,c) = f(r-1,c)

        有了状态转移方程,那我们就从起点出发,即从f(0,0)出发,而显然f(0,0)=1,接下来我们则只需要遍历每个位置,再根据状态转移方程给每个位置赋值即可。

代码实现如下:

    /**
     * @param m 代表网格行数
     * @param n 代表网格列数
     * @return 终点位置的路径总数
     */
    public int uniquePaths(int m, int n) {
        //每个位置的路径总数用一个二维数组存储
        int[][] data = new int[m][n];
        //给起点赋值
        data[0][0] = 1;
        //遍历每个位置
        for (int row = 0; row < m; row++) {
            for (int cloumn = 0; cloumn < n; cloumn++) {
                //非第一行第一列的位置
                if (row > 0 && cloumn > 0) {
                    data[row][cloumn] = data[row - 1][cloumn] + data[row][cloumn-1];
                //第一列的位置
                } else if (row > 0) {
                    data[row][cloumn] = data[row - 1][cloumn];
                //第一行的位置
                } else if (cloumn > 0) {
                    data[row][cloumn] = data[row][cloumn - 1];
                }
            }
        }
        //返回终点的路径总数
        return data[m - 1][n - 1];
    }