视频讲解:
动态规划中如何初始化很重要!| LeetCode:62.不同路径_哔哩哔哩_bilibili
动态规划,这次遇到障碍了| LeetCode:63. 不同路径 II_哔哩哔哩_bilibili
62. 不同路径
思路:和前一天的爬楼梯的思路基本一致,变化之处在于dp是二维数组,因此初始化也跟着进行了改变。
// 时间复杂度O(m*n)
// 空间复杂度O(m*n)class Solution {public int uniquePaths(int m, int n) {// 确定dp数组,数组的下标表示的是机器人所在的网格位置,数组的值是到达当前位置的方法数int[][] dp = new int[m][n];// 此外关于递推方程,其实上一个状态到下个状态之间如何进行转换是真的关键;昨天的最小cost爬楼梯是兼顾考虑cost值,所以转换需要加上cost,而简单的爬楼梯以及现在的机器人行走都是可以继承上一状态,因此没有转换的动作,所以需要加上什么,但是求取总的方法数量的要求使我们明确应该将所有的来源都进行总结求和// dp[i][j] = dp[i-1][j] + dp[i][j-1];// 由递推方程进行初始化,个人认为递推方程中自变量i所有无法满足进入递推方程的值都是初始化需要完成的内容Arrays.fill(dp[0], 1);for(int i=0; i<m; i++)dp[i][0] = 1;// 开始递推for(int i=1; i<m; i++){for(int j=1; j<n; j++)dp[i][j] = dp[i-1][j] + dp[i][j-1];}return dp[m-1][n-1];}
}
完成这道题之后,动态规划接触的第一个类型可以归纳为总数题,即计算到达某地的总的方法数,对于该类题目其状态转移没有额外需要相加的内容,也不是对于多个维度,多出来源去max或取min,而仅仅是将所有的来源都相加在一起作为当前位置的dp数组值即可;可能会存在的变化的就是有时候题目会增加条件,那么数组何时相加需要严格按照条件来,但是相加的操作始终不变。
另外针对动态规划的五步曲,个人认为还有这些细节需要补充:
- 声明定义dp数组,dp数组的类型与题目的返回类型保持一致,dp数组的维度与题目给的参照维度保持一致,如果是图表或者网格题,那么dp的维度以及长度与图表和网格保持一致;其次思考数组下标的含义;然后思考数组每个位置上值的含义;
- 基于数组下标含义及值的含义,拟定递推方程,没有什么特定的套路可以总结,唯独多刷题找感觉,但是有一点是,给出的参照量与递推方程中的状态转移非常的相关,需要思考是否需要加上参考量(昨天与今天做的总数题都没有状态转移的额外元素加入操作,但是昨天的最小代价题cost就是额外的参照量,需要思考从当前状态到下一状态这个cost要怎么用,使用前一状态下标对应的cost还是当前下标对应的cost,这些细节非常重要。所以状态转移一定要联系数组值含义、下标含义一同思索)
- 初始化,再思索出递推方程后,初始化就是为递推方程中不满足条件的自变量取值进行赋值操作,如dp[i]=……,i>0,那么i=0的dp所有元素就要进行初始化,这是我目前为止对于初始化的理解;然后初始化的值具体给多少一定要结合题意来给出
- 开始递推
- 如果不正确,打印dp,若与设想的一致,那么返回第二点;如果不一致,那么返回第四点;
63. 不同的路径||
思路:与不同的路径一样的思路,只不过增加了些条件,需要进行判断,这影响了初始化,以及dp遍历时的赋值,本题关键的地方在于初始化,一行或一列上如果存在障碍物,那么之后的位置就都不可以达到了,在初始化的时候一定要当作当前m或者n就是等于1来进行模拟与赋值。
// 时间复杂度O(m*n)
// 空间复杂度O(m*n)class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {// 离谱的特殊情况,直接障碍物放在起点了,但是已经没有必要了,原先出错是因为没有考虑到障碍物对于第一行与第一列遍历时的影响,现在已经在初始化操作上做出了应对if(obstacleGrid[0][0] == 1)return 0;int m = obstacleGrid.length;int n = obstacleGrid[0].length;// 确定dp数组,数组的下标表示访问到的位置,数组的值表示到达该位置的方法总数int[][] dp = new int[m][n];// 递推方程不变 dp[i][j] = dp[i-1][j] + dp[i][j-1];// 初始化 是处理i=0 和 j=0 的所有内容,但是需要特别注意的是,但一行一列上存在一个障碍物,那么后续的地方肯定就无法到达了,所以dp值是0,可以到达的地方是1for(int i=0; i<m; i++)if(obstacleGrid[i][0] == 0)dp[i][0] = 1;elsebreak;for(int j=0; j<n; j++)if(obstacleGrid[0][j] == 0)dp[0][j] = 1;elsebreak;// 开始递推,递推方程照用,也就增加了一个判断条件for(int i=1; i<m; i++){for(int j=1; j<n; j++){if(obstacleGrid[i][j] == 0)dp[i][j] = dp[i-1][j] + dp[i][j-1];elsedp[i][j] = 0;}}return dp[m-1][n-1];}
}