- Best Time to Buy and Sell Stock IV
Given an array prices and the i-th element of it represents the price of a stock on the i-th day.
You may complete at most k transactions. What’s the maximum profit?
Example
Example 1:
Input: k = 2, prices = [4, 4, 6, 1, 1, 4, 2 ,5]
Output: 6
Explanation: Buy at 4 and sell at 6. Then buy at 1 and sell at 5. Your profit is 2 + 4 = 6.
Example 2:
Input: k = 1, prices = [3, 2, 1]
Output: 0
Explanation: No transaction.
Challenge
O(nk) time. n is the size of prices.
Notice
You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).
解法1:DP。
思路参考自网上。
设两个DP数组,分别为global[n][k+1]和local[n][k+1]。
global[i][j]表示前i天完成了j笔交易,所得到的最优利润。注意第j笔交易不一定在第i天完成。
local[i][j]表示前i天完成了j笔交易,并且第j笔交易一定在第i天完成,所得到的最优利润。
转化方程为:
- global[i][j]=max(local[i][j],global[i-1][j]),
其中global[i-1][j]表示前i-1天共完成j笔交易(第i天并无交易)的最优利润,local[i][j]表示前i天共完成j笔交易并且第j笔在第i天完成的最优利润。显然,两者中的最大值为global[i][j]。 - local[i][j]=max(global[i-1][j-1], local[i-1][j])+ prices[i] - prices[i - 1],
因为根据local[i][j]的定义,第i天必须完成第j比交易,所以我们要考虑两种情况。一种是前i-1天已经做了j-1笔交易,
其中global[i-1][j-1]表示前i-1天共完成j-1笔交易(第i天并无交易)的最优利润,local[i-1][j]表示前i-1天共完成j笔交易并且第j天完成第i-1笔交易的最优利润。光global[i-1][j-1]+diff还不行,因为global[i-1][j-1]这个全局最优没把第j天考虑进去,所以要再考虑local[i-1][j]+diff,这个就是把第j比交易在第i-1天完成换成在第i天完成。
注意:网上好多答案都用了global[i-1][j-1]+max(diff, 0)。diff=prices[i] - prices[i - 1]。我认为直接用diff就可以。因为如果diff<0,那么下面用global[i][j]=global[i-1][j]就可以了,因为prices[i-1]比prices[i]大。
注意:
1)当k >= n / 2时,没有必要用O(n^2)的DP算法,直接用O(n)的累加即可。此时该问题转换为无限次交易的买股票问题。参考LintCode 150: Best Time to Buy and Sell Stock II
class Solution {
public:
/**
* @param K: An integer
* @param prices: An integer array
* @return: Maximum profit
*/
int maxProfit(int k, vector<int> &prices) {
int n = prices.size();
if (n == 0) return 0;
if (k >= n / 2) {
int result = 0;
for (int i = 1; i < n; ++i)
result += max(prices[i] - prices[i - 1], 0);
return result;
}
vector<vector<int>> global(n, vector<int>(k + 1, 0));
vector<vector<int>> local(n, vector<int>(k + 1, 0));
for (int i = 1; i < n; ++i) {
//for (int j = 1; j <= k; ++j) { // is also OK
for (int j = k; j >= 1; --j) {
local[i][j] = max(global[i - 1][j - 1], local[i - 1][j]) + prices[i] - prices[i - 1];
global[i][j] = max(global[i - 1][j], local[i][j]);
}
}
return global[n - 1][k];
}
};
解法2:解法1的空间简化版本,类似01背包的空间优化。
代码如下:
class Solution {
public:
/**
* @param K: An integer
* @param prices: An integer array
* @return: Maximum profit
*/
int maxProfit(int k, vector<int> &prices) {
int n = prices.size();
if (n == 0) return 0;
if (k >= n / 2) {
int result = 0;
for (int i = 1; i < n; ++i) {
if (prices[i] - prices[i - 1] > 0)
result += prices[i] - prices[i - 1];
}
return result;
}
vector<int> global(k + 1);
vector<int> local(k + 1);
for (int i = 1; i < n; ++i) {
for (int j = k; j >= 1; --j) {
local[j] = max(global[j - 1], local[j]) + prices[i] - prices[i - 1];;
global[j] = max(global[j], local[j]);
}
}
return global[k];
}
};
解法3:DP。
参考了 https://www.hrwhisper.me/leetcode-best-time-to-buy-and-sell-stock-i-ii-iii-iv/
这个DP是最容易想到的,中规中矩的一种DP。
dp[i][j]表示第i天完成了第j次交易,即该dp[][]相当于解法1/2中的local[][]。//注意,下面为了代码方便用dp[j][i]表示。
状态转移方程:
dp[i][x] = max(dp[i-1][x] , dp[j][x – 1] + prices[i] – prices[j]) 0 <= j < i
dp[i-1][x]为第i天不进行交易,dp[j][x – 1] + prices[i] – prices[j]为枚举j从0~i-1,第j天买入,第i天卖出。
该算法时间复杂度为O(n^2*k),当输入规模大的时候会超时。
代码如下:
class Solution {
public:
/**
* @param K: An integer
* @param prices: An integer array
* @return: Maximum profit
*/
int maxProfit(int k, vector<int> &prices) {
int n = prices.size();
if (n == 0) return 0;
int result = 0;
if (k >= n / 2) {
for (int i = 1; i < n; ++i)
result += max(prices[i] - prices[i - 1], 0);
return result;
}
vector<vector<int>> dp(n, vector<int>(k + 1, 0));
for (int i = 1; i <= k; ++i) {
for (int j = 1; j < n; ++j) {
dp[j][i] = dp[j - 1][i]; //initial value is no transaction on day j
for (int x = 0; x < j; ++x) {
dp[j][i] = max(dp[j][i], dp[x][i - 1] + prices[j] - prices[x]);
}
}
result = max(result, dp[n - 1][i]);
}
return result;
}
};
解法4:解法3的改进版本。
参考了 https://www.hrwhisper.me/leetcode-best-time-to-buy-and-sell-stock-i-ii-iii-iv/
解法3的每一个for x循环就是为了得到每次固定的i,j从1…n-1遍历所取的dp[j][i-1]-prices[j]的最大值。
那我们就可以直接用一个变量maxTemp来记录dp[j][i-1]-prices[j]对每个i所取的最大值。
另外,应该也可以做预处理记录这个数据。应该两层循环一个一维数组就够了。
时间复杂度改进到O(nk)。
class Solution {
public:
/**
* @param K: An integer
* @param prices: An integer array
* @return: Maximum profit
*/
int maxProfit(int k, vector<int> &prices) {
int n = prices.size();
if (n == 0) return 0;
int result = 0;
if (k >= n / 2) {
for (int i = 1; i < n; ++i)
result += max(prices[i] - prices[i - 1], 0);
return result;
}
vector<vector<int>> dp(n, vector<int>(k + 1, 0));
for (int i = 1; i <= k; ++i) {
//maxTmp records the maximum vlue of dp[j][i - 1] - prices[j], for fixed i, j=1..n-1
int maxTmp = -prices[0]; //dp[0][i - 1]-prices[0], j=0
for (int j = 1; j < n; ++j) {
dp[j][i] = max(dp[j - 1][i], maxTmp + prices[j]);
maxTmp = max(maxTmp, dp[j][i - 1] - prices[j]);
}
result = max(result, dp[n - 1][i]);
}
return result;
}
};
本文探讨了在给定股票价格序列和最多允许交易次数的情况下,如何计算最大可能利润的问题。介绍了四种不同的动态规划解法,包括标准DP、空间优化DP、二次时间复杂度DP及其改进版。每种解法都有详细的步骤说明和对应的代码实现。
503

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



