本次题目
- 509 斐波那契数
- 70 爬楼梯
- 746 使用最小花费爬楼梯
- 62 不同路径
- 63 不同路径II
- 343 整数拆分
- 96 不同的二叉搜索树
动态规划(DP)
- 针对同一问题的多个重叠子问题。
- 步骤:
- 确定dp数组及其下标的定义
- 确定递推公式
- 初始化dp数组
- 确定遍历顺序
- 举例推导dp数组
509 斐波那契数
- DP:
- 定义一维dp数组保存递归的结果,dp[i]为第i个斐波那契数;
- 递推公式(状态转移方程):dp[i] = dp[i-1] + dp[i-2];
- 初始化dp数组:dp[0] = 0,dp[1] = 1;
- 确定遍历顺序,i递增;
- 举例。
- 注意:dp数组实际存储两个值即可。
class Solution {
public int fib(int n) {
if (n <= 1) return n;
int[] dp = new int[n + 1];
dp[0] = 0;
dp[1] = 1;
for(int i = 2; i <= n; i++){
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
}
70 爬楼梯
- DP:
- dp[i]为爬到第i层楼梯的方法数量;
- dp[i] = dp[i-1] + dp[i-2];
- dp[1] = 1,dp[2] = 2;
- i递增遍历;
- 举例。注意:同斐波那契数列,不考虑i=0。
class Solution {
public int climbStairs(int n) {
if (n <= 1) return n;
int[] dp = new int[3];
dp[1] = 1;
dp[2] = 2;
for(int i = 3; i <= n; i++){
int sum = dp[1] + dp[2];
dp[1] = dp[2];
dp[2] = sum;
}
return dp[2];
}
}
746 使用最小花费爬楼梯
- DP:
- dp[i]为到达第i个台阶所花费的最少体力;
- dp[i] = min(dp[i-1], dp[i-2]) + cost[i];
- dp[0]=cost[0],dp[1] = cost[1];
- i递增遍历;
- 举例。
- 注意:dp数组记录两位即可。最后一步到达顶层不用花费。
class Solution {
public int minCostClimbingStairs(int[] cost) {
int[] dp = new int[cost.length];
dp[0] = cost[0];
dp[1] = cost[1];
for(int i = 2; i < cost.length; i++){
dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];
}
return Math.min(dp[cost.length - 1], dp[cost.length - 2]);
}
}
62 不同路径
- DP:
- 定义二维dp数组,dp[i][j]为从起点到点(i,j)的路径个数;
- dp[i][j] = dp[i-1][j] + dp[i][j-1];
- dp[0][j] = 1,dp[i][0] = 1;
- 从左到右从上到下i,j递增;
- 举例。
- 注意:使用一位dp数组即可。
- 补充:数论方法C_(m+n-2)^(m-1)
class Solution {
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
for(int j = 0; j < n; j++) dp[0][j] = 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];
}
}
63 不同路径II
- 同上,但是路径上增加了障碍。DP:
- 定义二维dp数组,dp[i][j]为从起点到点(i,j)的路径个数;
- dp[i][j] = dp[i-1][j] + dp[i][j-1],如果(i,j)为障碍则无法到达(dp[i][j] = 0);
- dp[0][j] = 1,dp[i][0] = 1,如果有障碍则为0,并且停止后面的赋值;
- 从左到右从上到下i,j递增;
- 举例。
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int[][] dp = new int[obstacleGrid.length][obstacleGrid[0].length];
for(int i = 0; i < obstacleGrid.length; i++){
if(obstacleGrid[i][0] == 1) break;
dp[i][0] = 1;
}
for(int j = 0; j < obstacleGrid[0].length; j++){
if(obstacleGrid[0][j] == 1) break;
dp[0][j] = 1;
}
for(int i = 1; i < obstacleGrid.length; i++){
for(int j = 1; j < obstacleGrid[0].length; j++){
if(obstacleGrid[i][j] != 1) dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[obstacleGrid.length - 1][obstacleGrid[0].length - 1];
}
}
343 整数拆分
- DP:
- dp[i]为数字i拆分后得到的最大乘积;
- j递增,取两数相乘,两数以上相乘的最大值,dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j});
- dp[2] = 1;
- i从3开始递增递增;
- 举例。
- 补充:贪心算法,将数字分为n个3,剩下4则保留并乘4。
class Solution {
public int integerBreak(int n) {
int[] dp = new int[n + 1];
dp[2] = 1;
for(int i = 3; i <= n; i++){
for(int j = 1; j < i - 1; j++){
dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
}
}
return dp[n];
}
}
96 不同的二叉搜索树
- DP:
- dp[i]为1到i节点组成的二叉搜索树的数量;
- dp[i] += dp[j - 1] * dp[i - j];
- dp[0] = 1;
- i,j递增;
- 举例。
class Solution {
public int numTrees(int n) {
int[] dp = new int[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];
}
}