动态规划算法的简单学习笔记
1、算法解释
动态规划(Dynamic Programming,DP)
- 查找很多重叠子问题的最优解
(最优子结构:局部最优解能决定全局最优解)- 将大问题重组为许多子问题
- 每个子问题的结果被保存,累积下来即为解决大问题、
- 关键是:状态转移方程
- 进行空间压缩,节省空间消耗
- 自上而下与自下而上
- 动态规划:自上而下,先解决子问题,再解决父问题;适合求最终状态的问题
- 带状态记录的优先搜索:自下而上,从父问题到子问题;适合输出所有符合的路径
2、一维动态规划
解题思路
- 题意为:爬上N阶楼梯共有多少种方法
- 分解为子问题:爬上第i阶楼梯有多少种方法,设为dp[i]
- 因为第i阶,可由i-1阶或i-2阶上来,所以有dp[i] = dp[i-1] + dp[i-2]
Java解答
class Solution {
public int climbStairs(int n) {
if(n <= 2) return n;
int[] dp = new int[n];
dp[0] = 1;
dp[1] = 2;
for (int i = 2; i < n; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n-1];
}
}
解题思路
- 对于第K间屋子是否打劫:
- 若打劫,则前面的累积至K-2间都打劫了
- 若不打劫,则仅打劫了前面累积至K-1间
- 到达当前屋子i时,设最大累积打劫金额为dp[i]
- 若第i间打劫,则dp[i] = dp[i-2]+nums[i]
- 若第i间不打劫,则dp[i] = dp[i-1]
Java解答
class Solution {
public int rob(int[] nums) {
int len = nums.length;
if (nums == null || len == 0) {
return 0;
}
if (len == 1) {
return nums[0];
}
int[] dp = new int[len];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for (int i = 2; i < len; i++) {
dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]);
}
return dp[len-1];
}
}
解题思路
- 注意:等差子数组,各元素在原数组中必须连续
- 每当有一个子数组时,总和累计
Java解答
class Solution {
public int numberOfArithmeticSlices(int[] nums) {
int len = nums.length;
int sum = 0;
int[] dp = new int[len];
for (int i = 2; i < len; i++) {
if (nums[i] - nums[i-1] == nums[i-1] - nums[i-2]) {
dp[i] = 1 + dp[i-1];
sum += dp[i];
}
}
return sum;
}
}
3、二维动态规划
解题思路
- 设当前位置的dp[i][j]为此前的路径最小和
- dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
- 由于对于已经遍历过的j列,不会再遍历
- 由此,可以通过空间压缩,将dp二维将为一位,提升算法
Java解答
class Solution {
public int minPathSum(int[][] grid) {
int row = grid.length, col = grid[0].length;
int[] dp = new int[col];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (i == 0 && j == 0) {
dp[j] = grid[i][j];
} else if (i == 0) {
dp[j] = dp[j-1] + grid[i][j];
} else if (j == 0) {
dp[j] = dp[j] + grid[i][j];
} else {
dp[j] = Math.min(dp[j], dp[j-1]) + grid[i][j];
}
}
}
return dp[col-1];
}
}
解题思路
- 需要四个方向的搜索
- 为避免重复遍历相同位置,从右下、左上两个方向两次遍历
Java解答
class Solution {
public int[][] updateMatrix(int[][] mat) {
int row = mat.length, col = mat[0].length;
int dp[][] = new int[row][col];
for (int i = 0; i < row; i++) {
Arrays.fill(dp[i], Integer.MAX_VALUE-1);
}
// 右下遍历一次
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (mat[i][j] == 0) {
dp[i][j] = 0;
} else {
if (j > 0) {
dp[i][j] = Math.min(dp[i][j], dp[i][j-1]+1);
}
if (i > 0) {
dp[i][j] = Math.min(dp[i][j]