leecode更新

This commit is contained in:
markilue 2022-12-13 14:49:17 +08:00
parent ec0018c737
commit 8be0b33907
2 changed files with 213 additions and 0 deletions

View File

@ -0,0 +1,131 @@
package com.markilue.leecode.dynamic;
import org.junit.Test;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.dynamic
* @Author: dingjiawen
* @CreateTime: 2022-12-13 09:47
* @Description:
* TODO 力扣123题 买卖股票的最佳时期III:
* 给定一个数组它的第 i 个元素是一支给定的股票在第 i 天的价格
* 设计一个算法来计算你所能获取的最大利润你最多可以完成 两笔 交易
* 注意你不能同时参与多笔交易你必须在再次购买前出售掉之前的股票
* @Version: 1.0
*/
public class T23_MaxProfit {
@Test
public void test(){
int[] prices={3, 3, 5, 0, 0, 3, 1, 4};
System.out.println(maxProfit2(prices));
}
/**
* 思路这题的核心差别是:最多可以完成两笔交易,本人没有啥思路被卡在怎么知道卖出了第一次了以下是代码随想录解法
* TODO 动态规划五部曲:核心在于dp的定义和初始化 ->越来越发现状态转移方程为什么叫状态 转移方程
* (观察第i天的情况可以发现,第i天可能有五种状态(从来没有操作;已经买了第一次股票;已经卖出了第一次股票;已经买入了第二次股票;已经卖出了第二次股票))
* (1)dp定义:dp[i][0]第i天从来没有操作获得的最大值;dp[i][1]第i天已经买了第一次股票获得的最大值;dp[i][2]第i天已经卖出了第一次股票获得的最大值,以此类推
* (2)dp状态转移方程:
* 1.dp[i][0] 从来没有操作
* dp[i][0]=0
* 2.dp[i][1] = 昨天就买了第一次了 今天才买第一次
* dp[i][1]=max(dp[i-1][1],-price[i])
* 3.dp[i][2] = 昨天就卖出了第一次 ;昨天买了第一次的状态今天才卖
* dp[i][2]=max(dp[i-1][2],dp[i-1][1]+price[i])
* 4.dp[i][3] = 昨天就买了第二次;昨天卖出的第一次的状态今天才买第二次
* dp[i][3]=max(dp[i-1][3],dp[i-1][2]-price[i])
* 5.dp[i][4] = 昨天就卖出了第二次;昨天买入的第二次状态,今天卖了
* dp[i][4]=max(dp[i-1][4],dp[i-1][3]+price[i])
* (3)dp初始化:dp[0][0]=0 第一天刚买入dp[0][1]=-price[0];第一天刚买刚卖dp[0][2]=0;第一天刚买刚卖又买dp[0][3]=-price[0];第一天刚买刚卖又买又卖dp[0][4]=0
* (4)dp遍历顺序:从前往后
* (5)dp举例推导:以输入[1,2,3,4,5]为例
* [0 1 2 3 4]
* i=0 0 -1 0 -1 0
* i=1 0 -1 1 -1 1
* i=2 0 -1 2 -1 2
* i=3 0 -1 3 -1 3
* i=4 0 -1 4 -1 4
* 速度击败43.68%内存击败91.87% 20ms
* @param prices
* @return
*/
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length][5];
dp[0][0]=0;
dp[0][1]=-prices[0];
dp[0][2]=0;
dp[0][3]=-prices[0];
dp[0][4]=0;
for (int i = 1; i < prices.length; i++) {
dp[i][1]=Math.max(dp[i-1][1],-prices[i]);
dp[i][2]=Math.max(dp[i-1][2],dp[i][1]+prices[i]);
dp[i][3]=Math.max(dp[i-1][3],dp[i-1][2]-prices[i]);
dp[i][4]=Math.max(dp[i-1][4],dp[i-1][3]+prices[i]);
}
return dp[prices.length-1][4];
}
/**
* 滚动数组优化
* 速度击败86.14%内存击败55.57% 2ms
* @param prices
* @return
*/
public int maxProfit1(int[] prices) {
int dp0=0;
int dp1=-prices[0];
int dp2=0;
int dp3=-prices[0];
int dp4=0;
for (int i = 1; i < prices.length; i++) {
dp1=Math.max(dp1,-prices[i]);
dp2=Math.max(dp2,dp1+prices[i]);
dp3=Math.max(dp3,dp2-prices[i]);
dp4=Math.max(dp4,dp3+prices[i]);
}
return dp4;
}
/**
* 评论区两次遍历dp法从高到低一次从低到高一次两者相加表示两次交易的最大利润总和取最大值即可
* 速度击败62.14%内存击败98.29%
* 十分的精妙本质上就是把两次买卖分成了两次T21的maxProfit
* 但无法推广到只允许k次交易
* @param prices
* @return
*/
public int maxProfit2(int[] prices) {
int max = 0;
int minVal = prices[0], maxVal = prices[prices.length-1];
int[] dp = new int[prices.length];//从低到高dp[i]表示第i天以及之前的区间所获得的最大利润
int[] dp2 = new int[prices.length];//从高到低dp2[i]表示第i天开始直到最后一天期间所获得的最大利润
dp[0] = -prices[0];
//这个循环本质上是T21的maxProfit,即寻找从前往后的一次买卖获得最大值
for(int i=1;i<prices.length;++i){
dp[i] = Math.max(dp[i-1], prices[i]-minVal);
minVal = Math.min(prices[i], minVal);
}
//这个循环本质上是获取第i天后的获得的最大值
for(int i=prices.length-2;i>=0;--i){
dp2[i] = Math.max(dp2[i+1], maxVal-prices[i]);
maxVal = Math.max(maxVal, prices[i]);
}
//把第i天之前的最大值和第i天之后的最大值相加找到最大值
for(int i=1;i<=prices.length-1;++i){
// System.out.println(dp[i-1]+","+dp2[i]);
max = Math.max(dp[i-1]+dp2[i], max);
}
//只买卖一次的和买卖两次的谁大
return Math.max(dp[prices.length-1], max);
}
}

View File

@ -0,0 +1,82 @@
package com.markilue.leecode.dynamic;
import org.junit.Test;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.dynamic
* @Author: dingjiawen
* @CreateTime: 2022-12-13 12:00
* @Description:
* TODO 力扣188题 买卖股票的最佳时期IV:
* 给定一个整数数组 prices 它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格
* 设计一个算法来计算你所能获取的最大利润你最多可以完成 k 笔交易
* 注意你不能同时参与多笔交易你必须在再次购买前出售掉之前的股票
* @Version: 1.0
*/
public class T24_MaxProfit {
@Test
public void test(){
int k = 2;
int[] prices = {3, 2, 6, 5, 0, 3};
System.out.println(maxProfit1(k,prices));
}
/**
* 思路:这题与T23十分的类似:只是将2次交易推广到k次交易事实上T23的状态转移方程也具有推广性
* TODO 动归五部曲:事实上k次交易可以推广到有2*k+1个状态(无操作,第一次买第一次卖...,第k次买第k次买)
* (1)dp定义:dp[i][0]表示无操作dp[i][2*k+1]表示第k次买dp[i][2*(k+1)]表示第k次卖
* (2)dp状态转移方程:
* 1.dp[i][2*k+1]=max(dp[i-1][2*k+1],dp[2*k]-prices[i])
* 1.dp[i][2*(k+1)]=max(dp[i-1][2*(k+1)],dp[2*k+1]+prices[i])
* (3)dp初始化: dp[0][i%2==0]=0;dp[0][i%2==1]=-prices[i]
* (4)dp遍历顺序:从前往后
* (5)dp举例推导:
* 速度击败99.9%内部击败36.8% 1ms
* @param k
* @param prices
* @return
*/
public int maxProfit(int k, int[] prices) {
int[][] dp = new int[prices.length][2 * k + 1];
for (int i = 0; i < 2 * k + 1; i++) {
dp[0][i] = i % 2 == 0 ? 0 : -prices[0];
}
for (int i = 1; i < prices.length; i++) {
for (int j = 0; j < k; j++) {
dp[i][2*j+1]=Math.max(dp[i-1][2*j+1],dp[i-1][2*j]-prices[i]);
dp[i][2*(j+1)]=Math.max(dp[i-1][2*(j+1)],dp[i-1][2*j+1]+prices[i]);
}
}
return dp[prices.length-1][2*k];
}
/**
* 滚动数组简化
* 速度击败58.3%内存击败78.2% 1ms
* @param k
* @param prices
* @return
*/
public int maxProfit1(int k, int[] prices) {
int[] dp = new int[2 * k + 1];
for (int i = 0; i < 2 * k + 1; i++) {
dp[i] = i % 2 == 0 ? 0 : -prices[0];
}
for (int i = 1; i < prices.length; i++) {
for (int j = 0; j < k; j++) {
dp[2*j+1]=Math.max(dp[2*j+1],dp[2*j]-prices[i]);
dp[2*(j+1)]=Math.max(dp[2*(j+1)],dp[2*j+1]+prices[i]);
}
}
return dp[2*k];
}
}