1.思想
自底向上、空间换时间——求解多阶段决策问题
- 状态定义:dp[i]爬到第i层的方法数
- 状态转移方程:dp[i] = dp[i-1]+dp[i-2]
- 初始化:dp[1]=1 dp[2]=2
- 输出:dp[n]
2.三个特性
重复子问题
最优子结构:规模更大的可以由小的最优解得出
无后效性:已经求出的子问题不受更大规模问题的影响
2.1 重复子问题
leetcode91
public int numDecodings(String s) {
//重复子问题
//1.状态定义dp[i],以位置i结尾有多少种组合方式
//2.状态转移方程dp[i] = dp[i-1]*s.substring(i, i + 1)是否合法 + dp[i-2]*s.substring(i-1, i + 1)是否合法
//3.初始化 dp[1] dp[2]
//4.输出dp[s.length]
int[] dp = new int[s.length()];
if (s.length() <= 1){
return illeagal(s.substring(0, 1)) ? 0 : 1;
}
dp[0] = illeagal(s.substring(0, 1)) ? 0 : 1;
dp[1] = dp[0]*(illeagal(s.substring(1, 2)) ? 0 : 1)
+ (illeagal(s.substring(0, 2)) ? 0 : 1);
if (s.length() <= 2){
return dp[1];
}
for (int i = 2; i < s.length(); i++) {
//dp[i-1]*s.substring(i, i + 1)是否合法+dp[i-2]*s.substring(i-1, i + 1)是否合法
dp[i] = dp[i - 1] * (illeagal(s.substring(i, i + 1)) ? 0 : 1)
+ dp[i - 2] * (illeagal(s.substring(i - 1, i + 1)) ? 0 : 1);
}
return dp[dp.length - 1];
}
public boolean illeagal(String s) {
if (s.charAt(0) == '0')
return true;
if (s.length() == 1 && ((Integer.parseInt(s) < 1 || Integer.parseInt(s) > 9)))
return true;
if (s.length() == 2 && ((Integer.parseInt(s) > 26)))
return true;
return false;
}
2.2 最优子结构
最优子结果--整体最优结果 零钱兑换
- 定义状态:dp[i]为凑i的最少硬币数
- 状态转移公式:dp[11] = min(dp[10]+1,dp[9]+1,dp[6]+1),即三种选择找一个最优解。
dp[amount] = min(dp[amount - coin[i]] + 1) for i in [0, len - 1] if coin[i] <= amount
- 初始值:假设凑不出来,初始化为一个不可能的数
- 输出dp[i]
public int coinChange(int[] coins, int amount) {
if (amount == 0)
return 0;
int[] dp = new int[amount + 1];
//都初始化为-1
Arrays.fill(dp, amount + 1);
dp[0] = 0;
//已经确定的硬币为1
for (int i = 0; i < coins.length; i++) {
if (coins[i] <= amount)
dp[coins[i]] = 1;
}
for (int i = 1; i < dp.length; i++) {
for (int j = 0; j < coins.length; j++) {
//可达性
if (coins[j] <= i && (dp[i- coins[j]] != amount+1))
dp[i] = Math.min(dp[i- coins[j]] + 1, dp[i]);
}
}
return dp[amount] == amount+1 ? -1 : dp[amount];
}
2.3 无后效性
已经计算的子问题结果不会被改变 62 不同路径 way(i, j) = ways(i - 1, j) * 1 + ways(i, j - 1) * 1
重点在初始化上
public int uniquePaths(int m, int n) {
int row = m;
int col = n;
int[][] dp = new int[row][col];
//
for (int i = 0; i < row; i++) {
dp[i][0] = 1;
}
for (int i = 0; i < col; i++) {
dp[0][i] = 1;
}
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[row - 1][col - 1];
}
打家劫舍 更加确切得状态定义 倒推,i位置偷条件下得最值和i位置不偷条件下得最值
public int rob(int[] nums) {
int len = nums.length;
if (len == 0) {
return 0;
}
if (len == 1) {
return nums[0];
}
// dp[i][0]:考虑区间 [0..i] ,并且下标为 i 的这个房屋不偷
// dp[i][1]:考虑区间 [0..i] ,并且下标为 i 的这个房屋偷
int[][] dp = new int[len][2];
// 初始化
dp[0][0] = 0;
dp[0][1] = nums[0];
// 递推开始
for (int i = 1; i < len; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]);
dp[i][1] = dp[i - 1][0] + nums[i];
}
return Math.max(dp[len - 1][0], dp[len - 1][1]);
}
作者:liweiwei1419
链接:<https://leetcode.cn/leetbook/read/learning-algorithms-with-leetcode/9wmq9r/>
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。