动态规划
递归:是自上而下的解决问题,假设最基本的问题已经解决了,在递归过程中可能会出现许多重复的子问题的求解过程,可以采用记忆化搜索的方式来优化重复子问题的不足
动态规划:是自底向上的解决一个问题,将原问题拆解成若干子问题,同时保证子问题的答案,使得每个子问题只求解一次,最终获得原问题的答案
动态规划 ==> 出现重叠子问题、最优子结构 ==> 采用记忆化搜索(自顶向下) 或 动态规划(自底向上)
动态规划思路:明确状态(dp数组中存储的值的具体含义)==> 状态初始化 ==> 状态转移方程求解
状态:即函数的定义 (dp数组中存储的值的具体含义);
状态转移:根据对状态的定义,确定状态的转移,dp[i] 如何由 dp[j] (j<i) 获得
70. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
class Solution {
public int climbStairs(int n) {
//爬第k阶台阶,可以是从第k-1阶爬上来,也可以是从第k-2阶爬上来,所以,爬上第k阶楼梯的方法为爬上第k-1阶楼梯的方法+爬上第k-2阶楼梯的方法
//动态规划实现
if(n==1){
return 1;
}
int[] dp = new int[n+1]; //dp[k]为爬上第k阶楼梯的方法数
dp[1] = 1;
dp[2] = 2;
for(int k=3; k<=n; k++){
dp[k] = dp[k-2]+dp[k-1];
}
return dp[n];
}
}
120. 三角形最小路径和
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
例如,给定三角形:[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。说明:
如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。
思路:dp解法,开辟一个int[] dp ,用以记录某一层每个元素到最底层的最短距离,复用这个dp数组。
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
//dp[i]表示某一层第i个元素到最底层的最短距离
//从最底层开始,向上推,每一层都要更新dp[i]中的值,直到最顶层只有一个元素,则dp[0]即为所求
int[] dp = new int[triangle.get(n-1).size()];
//初始化,最底层每个元素到最底层的的距离就是最底层每个元素本身的值
for(int i=0; i<triangle.get(n-1).size(); i++){
dp[i] = triangle.get(n-1).get(i);
}
//外层循环负责向上更新层数, 内层循环负责更新当前层每个元素到最底层的最小距离
for(int k=n-2; k>=0; k--){
//获得当前层的list,根据当前层list中的元素值以及下一层dp数组进行更新,使其dp成为当前层dp数组
List<Integer> cur = triangle.get(k);
for(int i=0; i<cur.size(); i++){
dp[i] = cur.get(i)+Math.min(dp[i], dp[i+1]);
}
}
return dp[0];
}
}
64.最小路径和
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
class Solution {
public int minPathSum(int[][] grid) {
if(grid.length == 0 || grid[0].length == 0){
return 0;
}
int m = grid.length, n = grid[0].length;
// 采用二维数组 dp[][], dp[i][k]表示走到grid[i][k]时的最短路径, 则 dp[i][k] = min(dp[i-1][k], dp[i][k-1])+grid[i][k]
// 由于考虑第i行的时候,前i-2行的数据都不需要的,可以复用一个一维数组即可
// dp[k] = min(dp[k-1], dp[k])+grid[i][k], 其中左侧的 dp[k]即dp[i][k], 右侧的 dp[k]即dp[i-1][k], 右侧的dp[k-1]即dp[i][k-1]
int[] dp = new int[n];
for(int i=0; i<m; i++){
for(int k=0; k<n; k++){
if(k==0){
dp[k] = dp[k];
}else if(i==0){
dp[k] = dp[k-1];
}else{
dp[k] = Math.min(dp[k], dp[k-1]);
}
dp[k] += grid[i][k];
}
}
return dp[n-1];
}
}
62.不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
例如,上图是一个7 x 3 的网格。有多少可能的路径?
说明:m 和 n 的值均不超过 100。
示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共