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 。 示例 2: 输入:prices = [1,2,3,4,5] 输出:4 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 示例 3: 输入:prices = [7,6,4,3,1] 输出:0 解释:在这个情况下, 没有交易完成, 所以最大利润为 0。 示例 4: 输入:prices = [1] 输出:0
思路
1. 确定dp数组(dp table)以及下标的含义
dp[i][k][0]:第i天完成了k笔交易并持有股票所能获得的最大利润。dp[i][k][1]:第i天完成了k笔交易并不持有股票所能获得的最大利润。
2. 确定递推公式
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k - 1][1] - prices[i]):第i天持有股票的最大利润是前一天持有股票的最大利润(即没有卖出)和前一天不持有股票但在第i天买入两者之间的较大值。dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k][0] + prices[i]):第i天不持有股票的最大利润是前一天不持有股票的最大利润(即没有买入)和前一天持有股票但在第i天卖出两者之间的较大值。
3. dp数组如何初始化
dp[0][k][0] = -prices[0]:第0天买入股票,所以利润为-prices[0]。dp[0][k][1] = 0:第0天不买入也不卖出,利润为0。
4. 确定遍历顺序
从第1天开始遍历,直到最后一天。k从1遍历到2,因为最多可以完成两笔交易。
5. 举例推导dp数组
以prices = [3, 3, 5, 0, 0, 3, 1, 4]为例,
- 第1天:
dp[1][1][0] = max(-3, 0 - 3) = -3,dp[1][1][1] = max(0, 3 + (-3)) = 0 - 第2天:
dp[2][1][0] = max(-3, 0 - 5) = -3,dp[2][1][1] = max(0, 5 + (-3)) = 2 - 第3天:
dp[3][1][0] = max(-3, 2 - 0) = 0,dp[3][1][1] = max(2, 0 + 0) = 2 - ...
- 第7天:
dp[7][2][0] = max(dp[6][2][0], dp[6][1][1] - 4) = max(-1, 2 - 4) = -1 - 第7天:
dp[7][2][1] = max(dp[6][2][1], dp[6][2][0] + 4) = max(3, -1 + 4) = 3
最终,dp[7][2][1] = 3 + 3 = 6,即最大利润为6。
题解
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (n == 0) return 0;
vector<vector<vector<int>>> dp(n, vector<vector<int>>(3, vector<int>(2)));
for (int i = 0; i < n; ++i) {
for (int k = 1; k <= 2; ++k) {
if (i == 0) {//初始化
dp[i][k][0] = -prices[i];
dp[i][k][1] = 0;
continue;
}
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k - 1][1] - prices[i]);
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k][0] + prices[i]);
}
}
return dp[n - 1][2][1];
}
};
188. 买卖股票的最佳时机 IV
给你一个整数数组 prices 和一个整数 k ,其中 prices[i] 是某支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1: 输入:k = 2, prices = [2,4,1] 输出:2 解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。 示例 2: 输入:k = 2, prices = [3,2,6,5,0,3] 输出:7 解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。 随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。
提示:
- 1 <= k <= 100
- 1 <= prices.length <= 1000
- 0 <= prices[i] <= 1000
思路
上一道题的题解代码是用于解决最多完成两笔交易的问题的。在这道题中,当问题变为最多完成k笔交易时,我们需要进行以下几个方面的修改:
- 更改dp数组的大小:在原代码中,
dp数组的第二维大小是3(k的值从0到2)。当k变为一个变量时,我们需要将这个维度的大小更改为k+1。 - 更改循环变量:在原代码中,循环变量
k是固定的(1和2)。现在,由于k是一个变量,我们需要更改循环以遍历从1到k。 - 更改返回值:在原代码中,返回值是
dp[n - 1][2][1],这里的2是固定的。现在,我们需要返回dp[n - 1][k][1],其中k是输入的变量。
题解
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int n = prices.size();
if (n == 0) return 0;
// 修改1:更改dp数组的第二维大小为 k+1
vector<vector<vector<int>>> dp(n, vector<vector<int>>(k + 1, vector<int>(2)));
for (int i = 0; i < n; ++i) {
// 修改2:更改循环变量,使其从 1 遍历到 k
for (int j = 1; j <= k; ++j) {
if (i == 0) { // 初始化
dp[i][j][0] = -prices[i];
dp[i][j][1] = 0;
continue;
}
// 以下逻辑与原代码相同,无需修改
dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j - 1][1] - prices[i]);
dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j][0] + prices[i]);
}
}
// 修改3:更改返回值,使其返回 dp[n - 1][k][1]
return dp[n - 1][k][1];
}
};