买卖股票的最佳时机III
题目链接:力扣
和昨天两道题相比,此题关键在于至多买卖两次
这意味着可以买卖一次,可以买卖两次,也可以不买卖。
- 确定dp数组以及下标的含义
一天一共就有五个状态,
没有操作
第一次持有股票
第一次不持有股票
第二次持有股票
第二次不持有股票
dp[i][j]中 i表示第i天,j为 [0 - 4] 五个状态,dp[i][j]表示第i天状态j所剩最大现金。
dp[i][1],表示的是第i天,买入股票的状态,并不是说一定要第i天买入股票 - 确定递推公式
达到dp[i][1]状态,有两个具体操作:
操作一:第i天买入股票了,那么dp[i][1] = dp[i-1][0] - prices[i]
操作二:第i天没有操作,而是沿用前一天买入的状态,即:dp[i][1] = dp[i - 1][1]
所以 dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1])
dp[i][2]也有两个操作:
操作一:第i天卖出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]
操作二:第i天没有操作,沿用前一天卖出股票的状态,即:dp[i][2] = dp[i - 1][2]
所以 dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])
同理可推出剩下状态部分:
dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]); - dp数组如何初始化
第0天没有操作,即:dp[0][0] = 0;
第0天做第一次买入的操作,dp[0][1] = -prices[0];
第0天做第一次卖出的操作,可以理解当天买入,当天卖出,所以dp[0][2] = 0;
第0天第二次买入操作,相当于第0天第一次买入了,第一次卖出了,然后再买入一次,所以第二次买入操作,初始化为:dp[0][3] = -prices[0];
同理第二次卖出初始化dp[0][4] = 0; - 确定遍历顺序
由递归公式其实已经可以看出,一定是从前向后遍历,因为dp[i],依靠dp[i - 1]的数值。 - 举例推导dp数组
class Solution {
public:
//dp[i][j] 表示第i天状态j所剩最大现金。
//没有操作 dp[i][0]
//第一次持有股票 dp[i][1]
//第一次不持有股票 dp[i][2]
//第二次持有股票 dp[i][3]
//第二次不持有股票 dp[i][4]
int maxProfit(vector<int>& prices) {
vector<vector<int>> dp(prices.size(), vector<int>(5,0));
dp[0][0] = 0;
dp[0][1] = -prices[0];
dp[0][2] = 0;
dp[0][3] = -prices[0]; //相当于第0天第一次买入了,第一次卖出了,然后再买入一次(第二次买入)
dp[0][4] = 0;
for(int i=1; i<prices.size();i++)
{
dp[i][0]= dp[i-1][0];
dp[i][1] = max(dp[i-1][1],dp[i-1][0]-prices[i]);
dp[i][2] = max(dp[i-1][2],dp[i-1][1]+ prices[i]);
dp[i][3] = max(dp[i-1][3],dp[i-1][2]-prices[i]);
dp[i][4] = max(dp[i-1][4],dp[i-1][3]+prices[i]);
}
return dp[prices.size()-1][4];
}
};
两次卖出的状态现金最大一定是最后一次卖出。如果第一次卖出已经是最大值了,那么我们可以在当天立刻买入再立刻卖出。所以dp[x][4]已经包含了dp[x][2]的情况。也就是说第二次卖出手里所剩的钱一定是最多的。
买卖股票的最佳时机IV
题目链接:力扣
这道题就是把上一题抽象化了,思路其实是一样的!直接贴上代码
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
vector<vector<int>> dp(prices.size(), vector<int>(2*k+1,0));
for(int i=0;i<2*k+1;i++)
{
if(i%2)
dp[0][i] = -prices[0];
}
for(int i=1; i<prices.size();i++)
{
dp[i][0] = dp[i-1][0];
for(int j=1; j<2*k+1;j++)
{
if(j%2) //持有股票
dp[i][j] = max(dp[i-1][j],dp[i][j-1]-prices[i]);
else //卖出股票
dp[i][j] = max(dp[i-1][j],dp[i][j-1]+prices[i]);
}
}
return dp[prices.size()-1][2*k+1-1];
}
};