Best Time to Buy and Sell Stock IV
Best Time to Buy and Sell Stock IV (leetcodelintcode)
Description
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most k transactions.
Notice
You may not engage in multiple transactions at the same time
(i.e., you must sell the stock before you buy again).
Example
Given prices = [4,4,6,1,1,4,2,5], and k = 2, return 6.
解题思路
本题使用动态规划求解,关键在于如何定义状态。
一、常规解法
- 定义状态:定义二维状态变量
f[i][j]为前i天至多进行j次交易,能够获得的最大收益。 - 定义状态转移函数:对于
f[i][j]上一个状态可能是f[x][j - 1], 0<x<i,第j次交易的利润为profit(x + 1, i),那么状态转移函数为f[i][j] = max{f[x][j - 1] + profit(x + 1, i)}, 0<x<i。 - 定义起点:
f[i][0] = 0, f[0][j] = Integer.MIN_VALUE。 - 定义终点:最终结果为
f[n][k]。
算法复杂度
- 时间复杂度:上述状态定义下,需要三重循环
i:0~n, j:0~k, x:0~(i-1),所以是O((n^2)*k)。 - 空间复杂度:
O(nk)。
二、优化时间解法
常规解法中定义的状态可以视为全局变量,可以增加一个变量保存局部变量,这样不必每次都对之前的状态进行 遍历。
- 定义状态:
mustsell[i][j]表示前i天,至多进行j次交易,第i天必须卖出,获得的最大收益。globalbest[i][j]表示前i天,至多进行j次交易,第i天可以不卖出,获得的最大收益。
- 定义状态转移函数:
- 定义
gain为第i - 1天买入,第i天卖出的收益,gain = prices[i] - prices[i - 1]。 mustsell[i][j] = Max{globalbest[i - 1][j - 1] + gain, mustsell[i - 1][j] + gain}。- 其中
globalbest[i - 1][j - 1] + gain = a. mustsell[i - 1][j - 1] + gain; b. mustsell[x][j - 1] + gain, x<i - 1。 - 对于情况
a,相当于i - 1天卖出,又买入,也就是把卖出拖延到第i天。所以可以是mustsell[i][j]的最优解,题目要求是至多交易k次,符合题意。 - 对于情况
b,本来其实要x遍历一遍0 ~ i-1找到mustsell[x][j - 1]的最优解。考虑到globalbest[i][j]已保存之前所有的最优解,所以时间得到优化。
- 其中
globalbest[i][j] = Max{globalbest[i - 1][j], mustsell[i][j]}。
- 定义
- 定义起点:
mustsell[0][i] = globalbest[0][i] = 0。 - 定义终点:
globalbest[n - 1][k]。
Java 实现
class
Solution
{
/**
*
@param
k: An integer
*
@param
prices: Given an integer array
*
@return
: Maximum profit
*/
public
int
maxProfit
(
int
k,
int
[] prices)
{
if
(prices ==
null
|| prices.length ==
0
|| k ==
0
) {
return
0
;
}
if
(k
>
= prices.length /
2
) {
int
profit =
0
;
for
(
int
i =
1
; i
<
prices.length; i++) {
if
(prices[i]
>
prices[i -
1
]) {
profit += prices[i] - prices[i -
1
];
}
}
return
profit;
}
int
n = prices.length;
// mustsell[i][j] 表示前i天,至多进行j次交易,第i天必须sell的最大获益
int
[][] mustsell =
new
int
[n +
1
][k +
1
];
// globalbest[i][j] 表示前i天,至多进行j次交易,第i天可以不sell的最大获益
int
[][] globalbest =
new
int
[n +
1
][k +
1
];
mustsell[
0
][
0
] = globalbest[
0
][
0
] =
0
;
for
(
int
i =
1
; i
<
= k; i++) {
mustsell[
0
][i] = globalbest[
0
][i] =
0
;
}
for
(
int
i =
1
; i
<
n; i++) {
int
gain = prices[i] - prices[i -
1
];
mustsell[i][
0
] =
0
;
for
(
int
j =
1
; j
<
= k; j++) {
mustsell[i][j] = Math.max(globalbest[i -
1
][j -
1
] + gain,
mustsell[i -
1
][j] + gain);
globalbest[i][j] = Math.max(globalbest[i -
1
][j], mustsell[i][j]);
}
}
return
globalbest[n -
1
][k];
}
};