所有股票问题(动规)
Posted 两片空白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了所有股票问题(动规)相关的知识,希望对你有一定的参考价值。
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
状态定义 :
可以用一个二维数组保持结果。二维数组有i行,两列。第一列代表持有股票的最大价值。第二列代表不持有股票的最大价值。
dp[i][0] 第i天持有股票的最大价值(最小成本)
dp[i][1] 第i天不持有股票的最大价值(最大收益)
转移方程:
持有股票,说明买入股票,价值应该是负的。如果当天买入股票为-price[i]。但是可以不是当天买入的股票。可以是在价格最低点买入的股票。持有股票的最大价值,就是最低点买入的股票。所以持有股票最大价值为:dp[i][0]=max(-price[i],dp[i-1][0])。当天买入与之前买入的最大值
不持有股票,说明是卖出股票,如果当天卖出股票,价值是dp[i][0]+price[i]。但是也可以不在当天卖出,可以在价格最高点的时候卖出。不持有股票时的最大价值就是在最高点卖出。所以不持有股票的最大值价值为:dp[i][1]=max(dp[i-1],dp[i][0]+price[i])。当天卖出的价值与之前卖出价值最大值。我的理解dp[i][0]+prices[i],当天卖出价值,为当天买入价值加上股票价值
初始化:
第一天持有股票,只能是买入股票,因为没有之前的对比。dp[1][0]=-price[0]
第一天不持有股票的最大价值,只能是将持有股票的卖出。dp[1][1]=dp[1][0]+price[0]=0
返回值:
最后一天不持有股票的最大价值dp[price.size()][1]。
class Solution {
public:
//用列为0的表示持有股票的最大价值,用列为1表示不持有股票的最大价值
int maxProfit(vector<int>& prices) {
int len=prices.size();
vector<vector<int>> dp(len+1,vector<int>(2,0));
//dp[0][0]=0;
//dp[0][1]=0;
//持有第一的股票,价值就是买入股票
dp[1][0]=-prices[0];
//卖出,价值为0
dp[1][1]=0;
//递推公式
for(int i=2;i<=len;i++){
dp[i][0]=max(dp[i-1][0],-prices[i-1]);
dp[i][1]=max(dp[i-1][1],prices[i-1]+dp[i][0]);
}
return dp[len][1];
}
};
122. 买卖股票的最佳时机 II (可多次买入卖出,但是买入前必须手里没有股票,卖出前手里只有一张股票)
给定一个数组
prices
,其中prices[i]
是一支给定股票第i
天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: prices = [7,1,5,3,6,4] 输出: 7 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
这题与上一题的解法如出一辙,但是由于可以多次买入卖出,转移方程有些不同。
转移方程:
持有股票的最大价值变了:由于可以多次买入卖出,可能之前手里的价值不为0了,可能已经有收益。所以当天买入股票时的价值不是-price[i],而是dp[i][1]-price[i],收益减去买入股票的前。但是持有股票的最大价值也鄙视一定当天买入,也是要在收益最大时,在价值最低点买入。所以持有股票的最大价值为 dp[i][0] = max(dp[i][0]-prices[i],dp[i-1][0])。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len=prices.size();
vector<vector<int>> dp(len+1,vector<int>(2,0));
//初始化
dp[1][0]=-prices[0];
dp[1][1]=0;
//转移方程
for(int i=2;i<=len;i++){
dp[i][0]=max(dp[i-1][1]-prices[i-1],dp[i-1][0]);
dp[i][1]=max(dp[i][0]+prices[i-1],dp[i-1][1]);
}
return dp[len][1];
}
};
123. 买卖股票的最佳时机 III (最多买两次)
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
这题相比较于上两题有点难,难的点在于最多完成两笔交易。
意思是,可以以不交易,可以交易一次,可以交易两次
状态定义:
我们可以定义5个状态:
dp[i][0]:第i天不交易最大价值
dp[i][1]:第i天第一次买入最大价值
dp[i][2]:第i天第一次卖出最大价值
dp[i][3]:第i天第二次买入最大价值
dp[i][4]:第i天第二次卖出最大价值
dp横坐标为天数,纵坐标为5种状态
转移方程:
有了上面两题,相信大家很容易得出转移方程:不如自己写一下
dp[i][0]不交易始终为0
dp[i][1]和dp[i][2]就像第一题一样。
dp[i][1]第一次买入,当前买入和之前买入的最大值。dp[i][0]=max(dp[i][0]-prices[i],dp[i-1][1])。
dp[i][2]第一次卖出,当前卖出价值和之前价值最大值。dp[i][1]=max(dp[i][1]+prices[i],dp[i-1][2])。
dp[i][3]和dp[i][4]就和第二题一样
dp[i][3]第二次买入,当前买入(已有利润)和之前买入的最大值。dp[i][0]=max(dp[i-1][2]-prices[i],dp[i-1][3])。
dp[i][4]第二次卖出,当前卖出价值和之前价值最大值。dp[i][1]=max(dp[i][3]+prices[i],dp[i-1][4])。
初始化:
第一天你不操作:dp[1][0]=0
第一天你买入:dp[1][1]=-prices[i-1]。
第一天你卖出:dp[1][2]=dp[1][1]+prices[i-1]=0
第二天你买入:dp[1][3]=dp[1][2]-prices[i-1]
第二天你卖出:dp[1][4]=dp[1][3]+prices[i-1]=0
返回值:
dp[prices.size()][4],最后不持有股票的最大价值。
这道题难的是状态定义,找出所有状态。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len=prices.size();
vector<vector<int>> dp(len+1,vector<int>(5,0));
//初始化
dp[1][0]=0;
dp[1][1]=-prices[0];
dp[1][2]=0;
dp[1][3]=-prices[0];
dp[1][4]=0;
//转移方程
for(int i=2;i<=len;i++){
dp[i][1]=max(-prices[i-1],dp[i-1][1]);
dp[i][2]=max(dp[i-1][2],dp[i][1]+prices[i-1]);
dp[i][3]=max(dp[i-1][2]-prices[i-1],dp[i-1][3]);
dp[i][4]=max(dp[i][3]+prices[i-1],dp[i-1][4]);
}
return dp[len][4];
}
};
给定一个整数数组
prices
,它的第i
个元素prices[i]
是一支给定的股票在第i
天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:k = 2, prices = [2,4,1] 输出:2 解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
这个题可以说是第3题的进阶版,将最多可以交易两次换成了最多可以交易k次。
解法还是和上一题一样,但是状态增加了。
状态定义:
我们可以定义5个状态:
dp[i][0]:第i天不交易最大价值
dp[i][1]:第i天第一次买入最大价值
dp[i][2]:第i天第一次卖出最大价值
dp[i][3]:第i天第二次买入最大价值
dp[i][4]:第i天第二次卖出最大价值
dp[i][5]:第i天第三次买入最大价值
dp[i][6]:第i天第三次卖出最大价值
.......
dp[i][2*k-1]:第i天第k次买入最大价值
dp[i][2*k]:第i天第k次卖出最大价值
列为奇数下标买入,列为偶数小标卖出。
dp横坐标为天数,纵坐标为状态
转移方程:
dp[i][0]不交易始终为0
每一次的买入都是上一次(不是天)的利润减去这天股票篇的价格与前一天的这一次买入价格最大值
dp[i][j]=max(dp[i][j-1]-prices[i],dp[i-1][j])
每一次卖出都是当天剩余的钱加上当天股票价值与上一天这一次的利润的最大值。
dp[i][j+1]=max(dp[i][j]+prices[i],dp[i-1][j+1])
初始化:
第一天所有的买入都是-prices[0],数组上是下标为奇数的,值为-prices[0]。
返回值:
dp[prices.size()][2*k],最后不持有股票的最大价值。
这道题难的是状态定义,找出所有状态。
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int len=prices.size();
if(len==0){
return 0;
}
vector<vector<int>> dp(len+1,vector<int>(2*k+1,0));
//初始化,奇数下标,j从1开始,更新j,j+=2
for(int j=1;j<2*k;j+=2){
dp[1][j]=-prices[0];
}
//递推公式
for(int i=2;i<=len;i++){
//更新j,j+=2,不是j=2*j+1,少了5
for(int j=1;j<2*k;j+=2){
dp[i][j]=max(dp[i-1][j],dp[i][j-1]-prices[i-1]);
dp[i][j+1]=max(dp[i-1][j+1],dp[i][j]+prices[i-1]);
}
}
return dp[len][2*k];
}
};
714. 买卖股票的最佳时机含手续费
给定一个整数数组
prices
,其中第i
个元素代表了第i
天的股票价格 ;整数fee
代表了交易股票的手续费用。你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
示例 1:
输入:prices = [1, 3, 2, 8, 4, 9], fee = 2 输出:8 解释:能够达到的最大利润: 在此处买入 prices[0] = 1 在此处卖出 prices[3] = 8 在此处买入 prices[4] = 4 在此处卖出 prices[5] = 9 总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8
这题和第二题的情况相同,只不过每次交易完毕后会要扣掉手续费。在代码中楼手续费在哪里体现呢?
在不持有股票中体现,如果当天卖出,则需要交手续费即dp[i][0]+prices[i-1]-fee。则在不持有股票的最大值由两种情况得到:
前一天不持有股票的价值与当天交易的价值即:dp[i][1]=max(dp[i-1][1],dp[i][0]+prices[i-1]-fee);
class Solution {
public:
//状态和第二题一样
int maxProfit(vector<int>& prices, int fee) {
int len=prices.size();
vector<vector<int>> dp(len+1,vector<int>(2,0));
dp[1][0]=-prices[0];
dp[1][1]=0;
for(int i=2;i<=len;i++){
dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i-1]);
dp[i][1]=max(dp[i-1][1],dp[i][0]+prices[i-1]-fee);
}
return dp[len][1];
}
};
309. 最佳买卖股票时机含冷冻期(一次交易后冷冻一天)
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
这题也是在第二题基础上,添加了一个冷冻期的条件。难的地方在于将所有情况都列举出来。
状态定义:
状态一:dp[i][0] :买入
卖出包括两种情况
状态二:dp[i][1]:之前已经卖出,已经度过冷冻期,但是不操作。
状态三:dp[i][2]:当天卖出
状态四:dp[i][3]:处于冷冻期
转移方程:
买入:
情况一:前一天为冷冻期 dp[i-1][3]-prices[i]。
情况二:前一天不是冷冻期,但是也没有卖出(处于状态二),dp[i-1][1]-prices[i]。
情况三:之前已经买入,dp[i-1][0]。
dp[i][0]=max(dp[i-1][0],max(dp[i-1][3]-prices[i],dp[i-1][1]-prices[i])
卖出:
状态二:之前已经卖出,已经度过冷冻期,但是不操作(前一天是冷冻期)。
情况一:前一天是冷冻期,dp[i-1][3]。
情况二:还是没有操作,dp[i-1][2]。
dp[i][1]=max(dp[i-1][3],dp[i-1][2])
状态三:当天卖出
情况一:当天卖出,dp[i][2]=dp[i][0]+prices[i]。
冷冻期:
情况一:前一天肯定是卖出:dp[i][3]=dp[i-1][2].
初始化:
第一天买入dp[1][0]=-prices[i-1],
第一天卖出dp[1][1]=0,dp[1][2]=0,第一天卖出价格收益也是0
冷冻期 dp[1][3]=0,第一天不存在冷冻期的情况
返回值 为卖出和冷冻期(可能请一天卖出最后一天是冷冻期)里最大的。
max(dp[len][1],max(dp[len][3],dp[len][2]));
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len=prices.size();
vector<vector<int>> dp(len+1,vector<int>(4,0));
//初始化
dp[1][0]=-prices[0];
dp[1][1]=0;
dp[1][2]=0;
dp[1][3]=0;
// dp[i][0] :买入
//dp[i][1]:之前已经卖出,已经度过冷冻期,但是不操作。
//dp[i][2]:当天卖出
//dp[i][3]:处于冷冻期
for(int i=2;i<=len;i++){
dp[i][0]=max(dp[i-1][0],max(dp[i-1][3]-prices[i-1],dp[i-1][1]-prices[i-1]));
dp[i][1]=max(dp[i-1][1],dp[i-1][3]);
dp[i][2]=dp[i][0]+prices[i-1];//当天一点是卖出,不需要于前一天比
dp[i][3]=dp[i-1][2];//前一天一定是卖出
}
return max(dp[len][1],max(dp[len][3],dp[len][2]));
}
};
以上是关于所有股票问题(动规)的主要内容,如果未能解决你的问题,请参考以下文章
代码随想录算法训练营第四十九天| 121 买卖股票的最佳时机 122 买卖股票的最佳时机II
NC41 最长无重复子数组/NC133链表的奇偶重排/NC116把数字翻译成字符串/NC135 股票交易的最大收益/NC126换钱的最少货币数/NC45实现二叉树先序,中序和后序遍历(递归)(代码片段