什么是动态规划算法?
按我自己的理解,动态规划是一种自底向上的思维,即最后一步的结果可由倒数第二步推导过来,倒数第二步可由倒数第三步推导过来,以此类推,直到推到第一步,而我们的解题思路则将顺序反过来,由第一步的结果推导到第二步,而第一步的结果一般都是已知的,即从已知结果出发,一步一步推导到最后一步。
当然,这里的推导是需要你写出状态转移方程,什么是状态转移方程呢?简单来说就是 第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];
}