题目地址:
https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/
给定每天的股票价格,以数组 p p p的形式给出,每天只能做一次交易并且每次只能买一股,问如果只允许做最多 k k k次买卖,那么最大收益是多少。
不妨设 p p p的长度为 n n n。首先,如果 k ≥ n / 2 k\ge n/2 k≥n/2,那么就相当于可以买卖无限次,因为买入和卖出的总次数小于等于 n n n。而买卖无限次是很容易的,只需要将每次股票上涨的差价都赚到即可。接下来考虑 k < n / 2 k< n/2 k<n/2的情况。
思路是状态机。规定两个状态,状态
1
1
1表示手中有股,
0
0
0表示手中无股。那么:
1、状态
1
1
1可以由状态
1
1
1达到,即昨天手里有股,然后持有一天到今天仍然有股,则边权为
0
0
0;状态
1
1
1可以由状态
0
0
0达到,即昨天手里无股,然后今天买入,状态变为有股,则边权为
−
p
[
i
]
-p[i]
−p[i];
2、状态
0
0
0可以由状态
0
0
0达到,即昨天手里无股,然后观望一天,今天仍然无股,则边权为
0
0
0;状态
0
0
0可以由状态
1
1
1达到,即昨天手里有股,然后今天卖出,状态变为无股,则边权为
+
p
[
i
]
+p[i]
+p[i]。
而题目相当于是要问,最多允许从两个状态转
k
k
k圈(表示买入
k
k
k次卖出
k
k
k次)的情况下,最大收益是多少。所以只需要将交易次数加入状态计算中即可。设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示以状态
1
1
1结尾的,到第
i
i
i天为止的(这里
i
i
i从
0
0
0计数),已经执行过最多
j
j
j次买入操作的情况下的最大收益,
g
[
i
]
[
j
]
g[i][j]
g[i][j]表示以状态
0
0
0结尾的,到第
i
i
i天为止的(这里
i
i
i从
0
0
0计数),已经执行过最多
j
j
j次买入操作的情况下的最大收益。则:
f
[
i
]
[
j
]
=
max
{
f
[
i
−
1
]
[
j
]
,
g
[
i
−
1
]
[
j
−
1
]
−
p
[
i
]
}
g
[
i
]
[
j
]
=
max
{
g
[
i
−
1
]
[
j
]
,
f
[
i
−
1
]
[
j
]
+
p
[
i
]
}
f[i][j]=\max\{f[i-1][j],g[i-1][j-1]-p[i]\}\\g[i][j]=\max\{g[i-1][j],f[i-1][j]+p[i]\}
f[i][j]=max{f[i−1][j],g[i−1][j−1]−p[i]}g[i][j]=max{g[i−1][j],f[i−1][j]+p[i]}那么要返回的就是
g
[
n
−
1
]
[
k
]
g[n-1][k]
g[n−1][k](原因是题目隐含要求是要从cash角度考虑,即要以无股的状态结尾,也就是股票不能持有到最后一天仍然不卖)。初始条件,首先我们知道
f
[
0
]
[
1
]
=
.
.
.
=
f
[
0
]
[
k
]
=
−
p
[
0
]
f[0][1]=...=f[0][k]=-p[0]
f[0][1]=...=f[0][k]=−p[0],这个表示如果第
0
0
0天就是以有股结尾,那么那天的cash净值当然是负的股价。由于
f
[
0
]
[
0
]
f[0][0]
f[0][0]这个状态是非法的(因为不可能经过了
0
0
0次买入操作却有股),为了使得这个状态转移到的答案一定被排除掉,我们令
f
[
0
]
[
0
]
=
−
∞
f[0][0]=-\infty
f[0][0]=−∞。代码如下:
class Solution {
public:
int maxProfit(int k, vector<int>& ps) {
int n = ps.size();
int res = 0;
if (k >= n / 2) {
for (int i = 1; i < n; i++) res += max(ps[i] - ps[i - 1], 0);
return res;
}
int buy[n][k + 1], sell[n][k + 1];
for (int i = 1; i <= k; i++) buy[0][i] = -ps[0];
buy[0][0] = -2e9;
memset(sell, 0, sizeof sell);
for (int i = 1; i < n; i++)
for (int j = 0; j <= k; j++) {
buy[i][j] = buy[i - 1][j];
if (j) buy[i][j] = max(buy[i][j], sell[i - 1][j - 1] - ps[i]);
sell[i][j] = max(sell[i - 1][j], buy[i - 1][j] + ps[i]);
}
return sell[n - 1][k];
}
};
时空复杂度 O ( n k ) O(nk) O(nk)。