矩阵类动态规划,也可以叫做坐标类动态规划,一般这类问题都会给你一个矩阵,矩阵里面有着一些信息,然后你需要根据这些信息求解问题.
文章目录
前言
其实 矩阵可以看作是图的一种,怎么说?你可以把整个矩阵当成一个图,矩阵里面的每个位置上的元素当成是图上的节点,然后每个节点的邻居就是其相邻的上下左右的位置,我们遍历矩阵其实就是遍历图,在遍历的过程中会有一些临时的状态,也就是子问题的答案,我们记录这些答案,从而推得我们最后想要的答案。 一般来说,在思考这类动态规划问题的时候,我们只需要思考当前位置的状态,然后试着去看当前位置和它邻居的递进关系,从而得出我们想要的递推方程,这一类动态规划问题,相对来说比较简单,我们通过几道例题来熟悉一下。一、leetcode第62号问题:不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
分析:因为每次的位置对应的路径总数可以由上位置和左位置的路径总和得到,所以可以得到递推方程为flag[ i ][ j ] = flag[ i-1 ] [ j ] + flag[ i ][ j-1 ];
题解:
class Solution {
public int uniquePaths(int m, int n) {
if(m==1&&n==1) return 1;//考虑特殊情况
int[][] flag = new int[m][n];
//因为边界点的上位置或左位置没有,因此上位置和左位置之和一定为1,而原点两者都没有,一定为0, 且无意义.
//初始化数组flag, 初始化为1
for(int i=1; i<m; i++){
flag[i][0] = 1;
}
for(int j=1; j<n; j++){
flag[0][j] = 1;
}
for(int i=1; i<m; i++){
for(int j=1; j<n; j++){
//填入递推方程
flag[i][j] = flag[i][j-1] + flag[i-1][j];
}
}
return flag[m-1][n-1];
}
}
二、leetcode第63号问题:不同路径II
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
说明:m 和 n 的值均不超过 100。
分析: 因为没有任何路径可以通过障碍物,所以经过障碍物的flag[ i ][ j ] = 0
题解:
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
if (obstacleGrid.length == 0 || obstacleGrid[0].length == 0) {
return 0;
}
if (obstacleGrid[0][0] == 1) {
return 0;
}
int m = obstacleGrid.length, n = obstacleGrid[0].length;
int[][] dp = new int[m][n];
dp[0][0] = 1;
for (int i = 1; i < m; ++i) {
dp[i][0] = obstacleGrid[i][0] == 1 ? 0 : dp[i - 1][0];
}
for (int i = 1; i < n; ++i) {
dp[0][i] = obstacleGrid[0][i] == 1 ? 0 : dp[0][i - 1];
}
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
dp[i][j] = obstacleGrid[i][j] == 1 ? 0 : dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
三、leetcode第64号问题:最小路径和
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
分析:与上面一样,只需要考虑当前位置的上位置和左位置即可,因为需要最短路径,所以选择最小的路径即可,递推方程为:flag[ i ][ j ] = min{ flag[ i-1 ][ j ], flag[ i ][ j-1 ] } + grid[ i ][ j ];
题解:
class Solution {
public int minPathSum(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
if(m==1&&n==1) return grid[0][0];
int[][] flag = new int[m][n];
int i, j;
flag[0][0] = grid[0][0];
for( i=1; i<m; i++){
//当前位置路径总数等于上一个位置加上当前位置路径数
flag[i][0] = flag[i-1][0] + grid[i][0];
}
for( j=1; j<n; j++){
//上同
flag[0][j] = flag[0][j-1] + grid[0][j];
}
for(i=1; i<m; i++){
for(j=1; j<n; j++){
flag[i][j] = Math.min(flag[i-1][j], flag[i][j-1])+ grid[i][j];
}
}
return flag[m-1][n-1];
}
}
四、leetcode第221号问题:最大正方型
在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。
分析:首先需要判断当前位置是否为1,即可以构成单位为1的正方形,若为1,则记录下当前正方形长度为1,然后以该点为正方形的右下角位置,向上位置, 左位置, 左上位置延申,利用取三个位置的可以构成正方形的最小长度与当前位置可以构成正方形的单位长度之和求出从该点出发可以构成正方形的长度, 若为0,则无法构成最小的单位正方形,记为0。递推方程为: flag[ i ][ j ] = min{ min{ flag[ i-1 ][ j ], flag[ i ][ j-1 ] }, flag[ i-1 ][ j-1 ] } + 1;
题解:
class Solution {
public int maximalSquare(char[][] matrix) {
int m = matrix.length;
int n = matrix[0].length;
int[][] flag = new int[m][n];
int i,j;
int maxLength = 0;
for(i=0; i<m; i++){
for(j=0; j<n; j++){
if(matrix[i][j] == '1'){
if(i==0||j==0){
flag[i][j] = 1;
}else{
flag[i][j] = Math.min(Math.min(flag[i-1][j], flag[i][j-1]), flag[i-1][j-1]) + 1;
}
maxLength = Math.max(maxLength, flag[i][j]);
}
}
}
return maxLength*maxLength;//面积
}
}
总结
对于矩阵类的动态规划,相对来说比较简单,这一类动态规划也比较好识别,一般输入的参数就是一个矩阵,解题的时候,我们只需要从当前位置出发考虑状态即可,通常来说当前位置的状态的求解仅仅需要借助其相邻位置的状态,通常我们也不需要考虑非常隐蔽的边界条件,一般需要做的初始化操作都可以从矩阵中,以及题目中的信息得出。