62.不同路径
和上楼梯相同,机器人只可以向右向下移动。所以在位置[i,j]有两种情况,一种从[i-1, j]向右移动到达当前位置,或者从[i, j-1]向下移动到当前位置。所以到达当前位置的方法数量就等于到达[i, j-1]和到达[i-1, j]的方法数量的和。
简单~
二维dp数组
- dp数组及下标含义: dp[i][j]表示从[0,0]到达[i,j]的方法数量
- 递推公式:dp[i][j] = dp[i-1][j] + dp[i][j-1]
- dp数组如何初始化:dp[][0]和dp[0][]都初始化为1,最上面那一行的位置只有一种方式就是从[0,0]一直向左移动,最左边那一列位置到达方式只有一种方式就是从[0,0]一直向下移动。
- 遍历顺序:从上往下,从左往右
- 打印dp数组
class Solution {
public:int uniquePaths(int m, int n) {if(m == 1 || n== 1) return 1;vector<vector<int>> dp(m, vector<int>(n));for(int i = 0; i < m; i++){for(int j = 0; j < n; j++){if(i==0 || j==0){dp[i][j] = 1;//dp数组初始化} else{dp[i][j] = dp[i-1][j]+dp[i][j-1];}}}return dp[m-1][n-1];}
};
class Solution {public int uniquePaths(int m, int n) {if(m == 1 || n == 1){return 1;}else{int[][] dp = new int[m][n];for(int i = 0; i < m; i++){for(int j = 0; j < n; j++){if(i==0 || j ==0){dp[i][j] = 1;}else{dp[i][j] = dp[i-1][j]+dp[i][j-1];}}}return dp[m-1][n-1];}}
}
class Solution(object):def uniquePaths(self, m, n):""":type m: int:type n: int:rtype: int"""if m == 1 or n == 1:return 1dp = [[0]*n]*mfor i in range(m):for j in range(n):if i == 0 or j == 0:dp[i][j] = 1else:dp[i][j] = dp[i-1][j] + dp[i][j-1]return dp[m-1][n-1]
参考文章
- https://programmercarl.com/0062.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84.html
63. 不同路径 II
和不同路径相同。就是初始化和递推公式需要改变一下。
NOTE:如果[0,0]或者[m, n]处有障碍物直接返回0。
二维dp数组
- dp数组及下标含义: dp[i][j]表示从[0,0]到达[i,j]的方法数量
- 递推公式:加个条件语句if obj[i][0]==0: dp[i][j] = dp[i-1][j] + dp[i][j-1] else: dp[i][j]=0
- dp数组如何初始化:遍历进行初始化第一行for(int i =0; i < m && obj[i][0]==0; i++) dp[i][0]=1//如果遇到障碍物就无法向右移动,后面的方法数都为0,第一列同理。
- 遍历顺序:从上往下,从左往右
- 打印dp数组
class Solution {
public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int m = obstacleGrid.size();int n = obstacleGrid[0].size();if(obstacleGrid[0][0]==1 || obstacleGrid[m-1][n-1]==1) return 0;vector<vector<int>> dp(m, vector<int>(n, 0));for(int i = 0; i < m && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1;for(int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;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];}}return dp[m-1][n-1];// int m = obstacleGrid.size();// int n = obstacleGrid[0].size();// if(obstacleGrid[0][0]==1 || obstacleGrid[m-1][n-1]==1) return 0;// vector<vector<int>> dp(m, vector<int>(n, 0));// for(int j = 0; j < n; j++){// if(obstacleGrid[0][j]==1) break;// else dp[0][j] = 1;// }// for(int i = 0; i < m; i++){// if(obstacleGrid[i][0]==1) break;// else dp[i][0] = 1;// }// 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];// }// }// }// return dp[m-1][n-1];}
};
class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int m = obstacleGrid.length;int n = obstacleGrid[0].length;if(obstacleGrid[0][0]==1 || obstacleGrid[m-1][n-1]==1) return 0;int[][] dp = new int[m][n];//初始化for(int i = 0; i < m && obstacleGrid[i][0]==0; i++){dp[i][0] = 1;}for(int j = 0; j < n && obstacleGrid[0][j]==0; j++){dp[0][j] = 1;}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];}else dp[i][j] = 0;}}return dp[m-1][n-1];}
}
class Solution(object):def uniquePathsWithObstacles(self, obstacleGrid):""":type obstacleGrid: List[List[int]]:rtype: int"""m = len(obstacleGrid)n = len(obstacleGrid[0])if(obstacleGrid[0][0] == 1 or obstacleGrid[m-1][n-1] == 1):return 0dp = [[0] * n for _ in range(m)]#注意这个初始化for i in range(m):if(obstacleGrid[i][0]==1):breakdp[i][0] = 1for j in range(n):if(obstacleGrid[0][j]==1):breakdp[0][j] = 1for i in range(1, m):for j in range(1, n):if obstacleGrid[i][j] == 1:dp[i][j] = 0else:dp[i][j] = dp[i-1][j] + dp[i][j-1]return dp[m-1][n-1]
参考文章
- https://programmercarl.com/0063.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84II.html
343. 整数拆分 (可跳过)
- dp数组及下标含义: dp[i]对i进行拆分,拆分之后得到的最大乘积为dp[i]
- 递推公式:从0到i/2(可以取到,因为拆分一个数我们比较的是dp[i],j*(i-j)和jdp[i-j]就是对i-j进一步拆分。其中j(i-j)是中间对称的,然后jdp[i-j]中dp[i-j]包含i-j小的拆分方式)遍历j,dp[i] = max(j(i-j), j*dp[i-j],dp[i]。求不同数据拆分方式下的最大乘积所以遍历j的时候要保存dp[i]当前最大值。
- dp数组如何初始化:0,1无法拆分没有意义都初始化为0,dp[2]=1
- 遍历顺序:从前往后
- 打印dp数组
class Solution {
public:int integerBreak(int n) {vector<int> dp(n+1);dp[0] = 0; dp[1] = 0; dp[2] = 1;//初始化for(int i = 3; i <= n; i++){for(int j = 1; j <= i/2; j++){//注意这里有等于dp[i] = max(dp[i], max(j*dp[i-j], j*(i-j)));//求的dp[i]最大值在j循环的时候要保存其中最大值}}return dp[n];}
};
class Solution {public int integerBreak(int n) {int[] dp = new int[n+1];dp[0] = 0; dp[1] = 0; dp[2] = 1;//dp数组初始化for(int i = 3; i <= n; i++){for(int j = 1; j <= i/2; j++){dp[i] = Math.max( dp[i] , Math.max(j*(i-j), j*dp[i-j]));}}return dp[n];}
}
class Solution(object):def integerBreak(self, n):""":type n: int:rtype: int"""dp = [0]*(n+1)#dp数组初始化dp[0] = 0 dp[1] = 0dp[2] = 1for i in range(3, n+1):for j in range(1, i/2+1):dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j]))return dp[n]
参考文章
- https://www.programmercarl.com/0343.%E6%95%B4%E6%95%B0%E6%8B%86%E5%88%86.html
96.不同的二叉搜索树 (可跳过)
不同二叉搜索树,固定头节点,二叉搜索树就是左子树的肿瘤*右子树的种类。对于给定n个节点,可以组成的二叉搜索树的种类就是这n个节点分别做头节点时候的可组成的二叉搜索树的种类的和。
- dp数组及下标含义: 给定i个节点有dp[i]种二叉搜索树。
- 递推公式:从0到i遍历j, dp[i]+=dp[j-1]*dp[i-j]
- dp数组如何初始化:dp[0]=1
- 遍历顺序:从小到大。
- 打印dp数组
class Solution {
public:int numTrees(int n) {vector<int> dp(n+1);dp[0] = 1;//初始化//递推公式for(int i = 1; i <= n; i++){for(int j = 1; j <= i; j++){dp[i] += dp[j-1]*dp[i-j];//左右子树}}return dp[n];}
};
class Solution {public int numTrees(int n) {int[] dp = new int[n+1];//dp数组初始化dp[0] = 1;//迭代公式for(int i = 1; i <= n; i++){for(int j = 1; j <=i; j++){dp[i] += dp[j-1]*dp[i-j];}}return dp[n];}
}
class Solution(object):def numTrees(self, n):""":type n: int:rtype: int"""dp = [0]*(n+1)dp[0] = 1for i in range(1, n+1):for j in range(1, i+1):dp[i] += dp[j-1]*dp[i-j]return dp[n]
参考文章
- https://www.programmercarl.com/0096.%E4%B8%8D%E5%90%8C%E7%9A%84%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91.html