文章目录
对于这类股票买卖问题,动态规划是一个常见且有效的解法。动态规划的核心在于找到状态表示和状态转移方程。对于股票买卖问题,通常有两个状态需要考虑:当前持有股票和当前不持有股票。针对特定的问题,可能还需要增加额外的状态来满足题目的限制条件,例如冷冻期和最大交易次数。
买卖股票的最佳时机含冷冻期
【题目描述】
给定一个整数数组prices
,其中第 prices[i]
表示第 _i_
天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
- 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
【输入样例】
prices = [1,2,3,0,2]
【输出样例】
3
【数据规模与约定】
1 <= prices.length <= 5000
0 <= prices[i] <= 1000
【解题思路】
- 状态表示
- 用
dp[i][0]
表示第i
天结束时持有股票所得最多现金,dp[i][1]
表示第i
天结束时不持有股票且处于冷冻期所得最多现金,dp[i][2]
表示第i
天结束时不持有股票且不处于冷冻期所得最多现金。
- 状态转移方程:
dp[i][0]
可以从前一天持有股票dp[i-1][0]
转移过来,或者前一天不持有股票且不处于冷冻期dp[i-1][2]
转移过来(即今天买入)。dp[i][1]
只能从前一天持有股票转移过来(即今天卖出),因此dp[i][1] = dp[i-1][0] + prices[i]
。dp[i][2]
可以从前一天不持有股票且不处于冷冻期dp[i-1][2]
转移过来,或者前一天不持有股票且处于冷冻期dp[i-1][1]
转移过来。
- 初始状态:
dp[0][0] = -prices[0]
,第一天买入股票。dp[0][1]
和dp[0][2]
都是 0,因为第一天结束时不可能处于冷冻期或有利润。
- 最终答案:
最后一天不持有股票的情况下利润最大,所以返回max(dp[n-1][1], dp[n-1][2])
。
【C++程序代码】
CPP
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
vector<vector<int> > dp(n,vector<int>(3));
dp[0][0] = -prices[0];
for (int i = 1; i < n; i++)
{
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][2]);
dp[i][2] = dp[i - 1][0] + prices[i];
}
return max(dp[n - 1][1], dp[n - 1][2]);
}
};
买卖股票的最佳时机含手续费
【题目描述】
给定一个整数数组 prices
,其中 prices[i]
表示第 i
天的股票价格 ;整数 fee
代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
**注意:**这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
【输入样例】
prices = [1, 3, 2, 8, 4, 9], fee = 2
【输出样例】
8
【数据规模与约定】
1 <= prices.length <= 5 * 104
1 <= prices[i] < 5 * 104
0 <= fee < 5 * 104
【解题思路】
用 dp[i][0]
表示第 i
天结束时持有股票所得最多现金,dp[i][1]
表示第 i
天结束时不持有股票所得最多现金。
【C++程序代码】
CPP
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
int n = prices.size();
vector<vector<int> > dp(n, vector<int>(2));
dp[0][0] = -prices[0];
for (int i = 1; i < n; i++)
{
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
dp[i][1] = max(dp[i - 1][0] - fee + prices[i], dp[i - 1][1]);
}
return dp[n - 1][1];
}
};
买卖股票的最佳时机III
【题目描述】
给定一个数组,它的第 i
个元素是一支给定的股票在第 i
天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
【输入样例】
prices = [3,3,5,0,0,3,1,4]
【输出样例】
6
【数据规模与约定】
1 <= prices.length <= 105
0 <= prices[i] <= 105
【解题思路】
对于每一天,我们考虑两种状态:
f[i][j]
表示第 i 天结束时,已经完成了 j 笔交易,并且手上持有一只股票的最大利润。g[i][j]
表示第 i 天结束时,已经完成了 j 笔交易,并且手上不持有股票的最大利润。
状态转移方程如下:
f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i])
表示今天我持有股票,可能是昨天就持有的,也可能是今天买的。g[i][j] = max(g[i - 1][j], f[i - 1][j - 1] + prices[i])
表示今天我不持有股票,可能是昨天就不持有的,也可能是今天卖出的。
考虑初始状态,第 0 天结束时:
f[0][0] = -prices[0]
表示第 0 天买入股票。g[0][0] = 0
表示第 0 天不操作。
由于最多进行两次交易,所以 j 的范围是 0 到 2。
最后的答案就是在最后一天,不持有股票且进行了 0、1、2 次交易中的最大值,即 max({ g[n - 1][0], g[n - 1][1], g[n - 1][2] })
。
【C++程序代码】
CPP
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
int k = 2;
vector<vector<int> > f(n, vector<int>(k + 1));
vector<vector<int> > g(n, vector<int>(k + 1));
f[0][0] = -prices[0];
g[0][0] = 0;
for (int i = 1; i < k + 1; i++)
{
f[0][i] = -0x3f3f3f3f;
g[0][i] = -0x3f3f3f3f;
}
for (int i = 1; i < n; i++)
{
for (int j = 0; j < k + 1; j++)
{
f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]);
g[i][j] = g[i - 1][j];
if (j > 0)
g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]);
}
}
return max({ g[n - 1][0],g[n - 1][1],g[n - 1][2] });
}
};
买卖股票的最佳时机IV
【题目描述】
给你一个整数数组 prices
和一个整数 k
,其中 prices[i]
是某支给定的股票在第 i
天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k
笔交易。也就是说,你最多可以买 k
次,卖 k
次。
**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
【输入样例】
k = 2, prices = [3,2,6,5,0,3]
【输出样例】
7
【数据规模与约定】
1 <= k <= 100
1 <= prices.length <= 1000
0 <= prices[i] <= 1000
【解题思路】
这个问题是前一个问题的泛化,解题思路是一样的,只是这里的 j 是从 0 到 k。
f[i][j]
表示第 i 天结束时,已经完成了 j 笔交易,并且手上持有一只股票的最大利润。g[i][j]
表示第 i 天结束时,已经完成了 j 笔交易,并且手上不持有股票的最大利润。
状态转移方程与上面一样。初始状态也类似,只是需要初始化所有可能的交易次数。
最后的答案是在最后一天,不持有股票且进行了 0 到 k 次交易中的最大值,即通过遍历 g[n - 1][j]
来找到最大值。
【C++程序代码】
CPP
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int n = prices.size();
vector<vector<int> > f(n, vector<int>(k + 1));
vector<vector<int> > g(n, vector<int>(k + 1));
f[0][0] = -prices[0];
g[0][0] = 0;
for (int i = 1; i < k + 1; i++)
{
f[0][i] = -0x3f3f3f3f;
g[0][i] = -0x3f3f3f3f;
}
for (int i = 1; i < n; i++)
{
for (int j = 0; j < k + 1; j++)
{
f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]);
g[i][j] = g[i - 1][j];
if (j > 0)
g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]);
}
}
int num_max = 0;
for (int i = 0; i < k + 1; i++)
{
num_max = max(g[n - 1][i], num_max);
}
return num_max;
}
};