题目
题目链接:188. 买卖股票的最佳时机 IV - 力扣(LeetCode)
给你一个整数数组 prices 和一个整数 k ,其中 prices[i] 是某支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。
**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
输入:k = 2, prices = [2,4,1]
输出:2
解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-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 。
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
}
};
思路
与 121. 买卖股票的最佳时机 - 力扣(LeetCode)和122. 买卖股票的最佳时机 II - 力扣(LeetCode)及123. 买卖股票的最佳时机 III - 力扣(LeetCode)的区别:
- 121题:只能交易一次,买入时之前没有利润,所以是
-prices[i]; - 122题:可以多次交易,买入时可以用之前交易获得的利润,所以是
dp[i-1][1] - prices[i]; - 123题:最多完成两笔交易。
- 188题:最多可以完成
k笔交易。
本题可以说是123题的进阶版,要求最多完成k笔交易。
1. DP数组含义
在123. 买卖股票的最佳时机 III - 力扣(LeetCode)题中,采用标准二维 DP,需要两个维度:
- 时间维度:第
i天 - 状态维度:第 k 次持有或不持有股票(123题中 k = 2,本题 k 不固定)
dp[i][j]中 i 表示第 i 天,j 为 [0-4]五个状态,dp[i][j]表示第 i 天状态 j 的最大利润:
j = 0:没有操作j = 1:第一次持有股票j = 2:第一次不持有股票j = 3:第二次持有股票j = 4:第二次不持有股票- …
所以: dp[i][1]:第i天,第一次持有股票,能获得的最大利润dp[i][2]:第i天,第一次不持有股票,能获得的最大利润
可以看出,除 j = 0 以外,当 j 为奇数时就是持有,j 为偶数时就是不持有。
题目要求是至多有K笔交易,那么j的范围就定义为 2 * k + 1 就可以了。
2. 递推公式
状态j = 0:没有操作
所以:dp[i][0] = dp[i-1][0];
状态j = 1:第一次持有股票
持有股票有两种可能:
- 第
i - 1天就持有,第i天继续持有:dp[i-1][1] - 第
i天买入:可以多次交易, 买入时可以使用之前交易获得的利润,相当于之前的总利润 - 当前股价,即dp[i - 1][0] - prices[i]。
递推公式:dp[i][1] = max(dp[i-1][1], dp[i - 1][0] - prices[i])
状态j = 2:第一次不持有股票
不持有股票有两种可能:
- 第
i - 1天就不持有,第i天继续不持有:dp[i-1][2] - 第
i天卖出:卖出前的状态是持有股票, 所以是dp[i-1][1] + prices[i]
递推公式:dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i])
状态j = 3:第二次持有股票
持有股票有两种可能:
- 第
i - 1天就持有,第i天继续持有:dp[i-1][3] - 第
i天买入:可以多次交易, 买入时可以使用之前交易获得的利润,相当于之前的总利润 - 当前股价,即dp[i - 1][2] - prices[i]。
递推公式:dp[i][3] = max(dp[i-1][3], dp[i - 1][2] - prices[i])
状态j = 4:第二次不持有股票
不持有股票有两种可能:
- 第
i - 1天就不持有,第i天继续不持有:dp[i-1][4] - 第
i天卖出:卖出前的状态是持有股票, 所以是dp[i-1][3] + prices[i]
递推公式:dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i])
根据以上可以推导出,递推公式:
for(int j = 0; j < 2 * k - 1; j += 2){
dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
}
3. DP数组初始化
dp[0][0]:没有操作,所以dp[0][0] = 0;dp[0][1]:第 0 天第一次持有股票, 所以dp[0][1] = -prices[0];dp[0][2]:第 0 天第一次不持有股票, 所以dp[0][2] = 0;(即当天买入,当天卖出)dp[0][3]:第 0 天第二次持有股票, 所以dp[0][3] = -prices[0];(第 0 天第一次买入,第一次卖出,然后再买入一次(第二次买入))dp[0][4]:第 0 天第二次不持有股票, 所以dp[0][4] = 0;
可以推出 dp[0][j] 当 j 为奇数时都初始化为 -prices[0]
for(int j = 1; j < 2 * k; j += 2){
dp[0][j] = -prices[0];
}
4. 确定遍历顺序
从递推公式可以看出 dp[i]都是由dp[i - 1]推导出来的,那么一定是从前向后遍历。
5. 打印DP数组
以输入[1,2,3,4,5],k=2为例。

最后一次卖出,一定是利润最大的,dp[prices.size() - 1][2 * k]即红色部分就是最后求解。
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <sstream>
#include <cmath>
using namespace std;
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int len = prices.size();
if(len == 0) return 0;
// 标准二维 dp
vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0));
// dp[0][0] = 0; // 无任何操作
// dp[0][1] = -prices[0]; // 第 0 天、第一次持有股票
// dp[0][2] = 0; // 第 0 天、第一次不持有股票
// dp[0][3] = -prices[0]; // 第 0 天、第二次持有股票
// dp[0][4] = 0; // 第 0 天、第二次不持有股票
for(int j = 1; j < 2 * k; j += 2){
dp[0][j] = -prices[0];
}
for(int i = 1; i < len; i++){
for(int j = 0; j < 2 * k; j += 2){
dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]); // 持有股票
dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]); // 不持有股票
}
}
for(int i = 0; i < len; i++){
for(int j = 0; j < 2 * k + 1; j++){
cout << dp[i][j] << " ";
}
cout << endl;
}
cout << endl;
return dp[len - 1][2 * k];
}
};
vector<int> inputvector(){
string line;
getline(cin, line);
istringstream iss(line);
vector<int> vec;
int num;
while(iss >> num) {
vec.push_back(num);
}
return vec;
}
int main(){
cout << "prices: ";
vector<int> prices = inputvector();
cout << "k: ";
int k;
cin >> k;
Solution obj;
int res = obj.maxProfit(k, prices);
cout << "res: " << res << endl;
return 0;
}
prices: 1 2 3 4 5
k: 2
0 -1 0 -1 0
0 -1 1 -1 1
0 -1 2 -1 2
0 -1 3 -1 3
0 -1 4 -1 4
res: 4
1186

被折叠的 条评论
为什么被折叠?



