给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:k = 2, prices = [2,4,1]
输出:2
解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv
不管使用几维数组,重要的是:
①将多少种状态分析情况;
②将状态表示完整;
③状态的转移关系弄清楚!
动态规划的重点
★理解:第几次交易的概念!
第一次交易:需要买了又卖了!并不是第一次没有买,这属于第0次交易!
分清楚:买在卖之前;完成一次买和卖,才算是完成了一次交易!
1.三维数组解决问题
class Solution {
public int maxProfit(int k, int[] prices) {
if(prices == null || prices.length == 0) return 0;
//三种解答方式:一维、二维、三维,都是为了将每天 + k次交易的所有状态情况表示出来
//①三维数组
int len = prices.length;
//这里交易数k的数组维度设置为 k+1表示,对边界情况好处理
int[][][] dp = new int[len][k + 1][2];//len表示天数;k表示交易次数;2表示此时手里有没有股票
//初始化,设置的是第一天时,出现不同状态的情况
for(int i = 0;i < k + 1;i++){
dp[0][i][0] = 0;//手里不持有股票
dp[0][i][1] = -prices[0];
}
for(int i = 1;i < len;i++){
for(int j = 1;j < k + 1;j++){
dp[i][j][1] = Math.max(dp[i - 1][j][1],dp[i - 1][j - 1][0] - prices[i]);//第j次交易:此时是买入;只和前一天第j次买入 或者 前一天第j-1次交易卖出有关!
dp[i][j][0] = Math.max(dp[i - 1][j][0],dp[i - 1][j][1] + prices[i]);//第j次交易:此时已经卖出:只和前一天第j次交易卖出 或者 前一天第j次交易买入有关!
}
}
return dp[len - 1][k][0];
}
}
2.二维数组解决问题
class Solution {
public int maxProfit(int k, int[] prices) {
if(prices == null || prices.length == 0 || k== 0) return 0;
int len = prices.length;
//三种解答方式:一维、二维、三维,都是为了将每天 + k次交易的所有状态情况表示出来
//②二维数组:考虑到状态转移方程中,dp[i]都是和前一天的dp[i - 1]有关,可以将维度降1
int[][] dp = new int[k + 1][2];
//这表示的是第一天的情况
for(int i = 1;i < k + 1;i++){
dp[i][1] = -prices[0];//1代表第k次买了
dp[i][0] = 0; //0代表第k次没买
}
for(int i = 1;i <= len;i++){
//第一个下标代表,交易的次数
//此处和上一天是有关系的!看从哪一天开始!
//这里的数值
dp[1][1] = Math.max(dp[1][1],-prices[i - 1]);//之前就有,或者现在这天的价钱更低!
dp[1][0] = Math.max(dp[1][0],dp[1][1] + prices[i -1]);//保持之前就没有的状态;或者之前有,卖掉了
for(int j = 2;j < k + 1;j++){
dp[j][1] = Math.max(dp[j][1],dp[j - 1][0] - prices[i - 1]);
dp[j][0] = Math.max(dp[j][0],dp[j][1] + prices[i - 1]);
}
}
return dp[k][0];
}
}
想清楚买和卖的关系;以及和上一次交易、前一天情况的联系,可以更清楚!对2的改进
class Solution {
public int maxProfit(int k, int[] prices) {
if(prices == null || prices.length == 0 || k== 0) return 0;
int len = prices.length;
//②二维数组:考虑到状态转移方程中,dp[i]都是和前一天的dp[i - 1]有关,可以将维度降1
int[][] dp = new int[k + 1][2];
//这表示的是 第一天 的情况:初始化
for(int i = 1;i < k + 1;i++){
dp[i][1] = -prices[0];//1代表第k次买了
dp[i][0] = 0; //0代表第k次没买
}
//注意:第0次交易的情况暗含在里面:都是0
for(int i = 1;i < len;i++){
//从第一次交易,开始赋值!
for(int j = 1;j < k + 1;j++){
//1是买
dp[j][1] = Math.max(dp[j][1],dp[j - 1][0] - prices[i]);
//0是买了又卖了
dp[j][0] = Math.max(dp[j][0],dp[j][1] + prices[i]);
}
}
return dp[k][0];
}
}
3.两个一维数组
class Solution {
public int maxProfit(int k, int[] prices) {
if(prices == null || prices.length == 0 || k== 0) return 0;
int len = prices.length;
//两个一维数组
//将数组长度设置为 k + 1,因为边界条件的考虑,需要由交易0次的情况
int[] buy = new int[k + 1];
int[] sell = new int[k + 1];
//第一天只有一支股票时的情况,初始化
for(int i = 1;i <= k;i++){
buy[i] = -prices[0];
}
for(int i = 1;i < len;i++){
for(int j = 1;j <= k;j++){
//保持前一天,当前交易次数时的情况,和前一天 前一次交易卖出了,当前又买的情况
buy[j] = Math.max(buy[j],sell[j - 1] - prices[i]);
sell[j] = Math.max(sell[j],buy[j] + prices[i]);
}
}
return sell[k];
}
}