动态规划
正向递归,记录每一次执行的结果,用于下一次计算
可以用来解决的问题:
- 需要大量遍历寻找最优解 框架公式
- 单个数组或者字符串要用动态规划时,可以把动态规划 dp[i] 定义为 nums[0:i] 中想要求的结果
- 当两个数组或者字符串要用动态规划时,可以把动态规划定义成两维的 dp[i][j] ,其含义是在 A[0:i] 与 B[0:j] 之间匹配得到的想要的结果
- 1.状态定义
- 2.状态转移方程
- 3.状态的初始化
- 4.遍历方向与范围
- 5.最终返回结果
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4
关键点
- 动态规划:dp[i]=max(dp[j])+1,其中0≤j<i且num[j]<num[i]
public int lengthOfLIS(int[] nums) {
if (nums.length == 0) {
return 0;
}
int[] dp = new int[nums.length];
dp[0] = 1;
int maxans = 1;
for (int i = 1; i < nums.length; i++) {
dp[i] = 1;
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
maxans = Math.max(maxans, dp[i]);
}
return maxans;
}
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
关键点
- 动态规划
//amount=11,可以从dp[10],dp[9],dp[6]3个地方到达
public int coinChange(int[] coins, int amount) {
if (amount == 0) return 0;
//(1)初始化DP table,零钱为0则硬币0个
int[] dp = new int[amount + 1];
dp[0] = 0;
//(2)遍历每种【状态】即金额,【选择】即保存最少硬币数量
for (int amounttt = 1; amounttt <= amount; amounttt++) {
//(1.1)初始化DP table。初始为最大硬币数量即amount个,因为最小面额为1元,那么+1即为无法到达的硬币个数。当然取(amount+1,Integer.MAX_VALUE - 1)(ps:后面存在1+dp[balance]计算)范围都可
dp[amounttt] = amount + 1;
//(3)遍历每种面额的硬币
for (int coin : coins) {
//(3.1)剩余金额balance,为当前amount-某额度硬币
int balance = amounttt - coin;
//(3.2)剩余额度>=0,说明该种硬币的组合可行
if (balance >= 0) {
//(3.3)说明amount额度足以减去某额度硬币。balance<amounttt,balance于前面已经计算保存好【状态】,+1为加上当前硬币
//例如1:coins[1,2,5],amounttt=4
//①balance=4-1,此时dp[4]=min(初始值,1+dp[3]),而dp[3]由1元+2元组合为2枚。保存最小值dp[4]=3
//②balance=4-2,此时dp[4]=min(初始值,1+dp[2]),而dp[2]由2元组合为1枚。保存最小值dp[4]=2
//③balance=4-5,不足以减去该面额
//例如2:coins[3],amounttt=5。dp则为[0,max,max,1,max,max,max],毕竟max<max+1
dp[amounttt] = Math.min(dp[amounttt], 1 + dp[balance]);
}
}
}
//(4)如amount等于初始值,说明在给定种类的硬币中无法组合成amount元。比如coin[2,3] amount=1,4...
return dp[amount] == (amount + 1) ? -1 : dp[amount];
}
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3
关键点
- 动态规划
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length(), n = text2.length();
//初始化为+1是为了当dp[0][0]时可以表示的为空字符串和另外一个字符串的匹配
int[][] dp = new int[m + 1][n + 1];
for (int i = 1; i <= m; i++) {
char c1 = text1.charAt(i - 1);
for (int j = 1; j <= n; j++) {
char c2 = text2.charAt(j - 1);
if (c1 == c2) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6
关键点
- 动态规划
public int maxSubArray(int[] nums) {
int pre = 0, maxAns = nums[0];
for (int x : nums) {
pre = Math.max(pre + x, x);
maxAns = Math.max(maxAns, pre);
}
return maxAns;
}
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6
关键点
- 动态规划
public int maxProfit(int prices[]) {
int minprice = Integer.MAX_VALUE;
int maxprofit = 0;
for (int i = 0; i < prices.length; i++) {
if (prices[i] < minprice) {
minprice = prices[i];
} else if (prices[i] - minprice > maxprofit) {
maxprofit = prices[i] - minprice;
}
}
return maxprofit;
}