"给血雨腥风的二级市场留下八个大字——巴菲特就那么回事"
题目链接:188. 买卖股票的最佳时机4 。给定一个整数数组 prices
,它的第 i
个元素 prices[i]
是一支给定的股票在第 i
天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
输入:
输出:
解释: 在第 天 (股票价格为 ) 的时候买入,在第 天 (股票价格为 ) 的时候卖出,这笔交易所能获得利润 。
输入:
输出:
解释: 在第 天 (股票价格为 ) 的时候买入,在第 天 (股票价格为 ) 的时候卖出, 这笔交易所能获得利润 = 。随后,在第 天 (股票价格 = ) 的时候买入,在第 天 (股票价格 = ) 的时候卖出, 这笔交易所能获得利润 = 。
在股票系列的前三期中,我们已经找到了买卖股票问题的最优子结构,即 表示第 天、股状态为 、交易次数为 时的最大利润,其中
该最优子结构同样适用于本题,仅仅是 的范围变成了 , 为题设中给出的具体交易次数。
中规中矩的动态规划
1、确定 dp 状态数组与含义
定义 为第 天、股状态为 、交易次数为 时的最大利润,其中,
-
当前交易日 ,;
-
是否持股 , 指不持股, 指持股;
-
交易次数 ,其中 为最多交易的次数(题设给出)。
2、确定 dp 状态转移方程
- 第 天,未持股 ,交易 次,其对应的状态为 ,从前一天的状态转移到当前的状态共有两种可能
-
第 天,未持股 ,交易已达 次,即
-
第 天,有持股 ,交易 次,在第 天(当日)卖出,即
因此,当日的状态转移方程为
- 第 天,有持股 ,交易 次,其对应的状态为 ,从前一天的状态转移到当前的状态共有两种可能
-
第 天,有持股 ,交易 次,即
-
第 天,未持股 ,交易 次,在第 天(当日)买入,即
因此,当日的状态转移方程为
3、确定 dp 初始状态
- 第 天,不持股状态
-
,代表第 天,不持股,交易次数为 时,我们手上的现金
-
[循环计算],代表第 天,不持股,交易次数为 时,我们手上的利润(这是一个非法状态,设置成代码运行时环境内的最小安全整数即可)
- 第 天,持股状态
-
,代表第 天,有持股,交易次数为 时,我们手上的最大利润(一买一卖才算一次交易,目前仅为买入,等这一股卖出时,交易次数再加 )
-
[循环计算],代表第 天,有持股,交易次数为 时,我们手上的最大利润(这是一个非法状态,设置成代码运行时环境内的最小安全整数即可)
- 第 天,交易 次的状态
-
,不管是第几天,没有持股也没有交易,那利润比为0
-
,[循环计算],代表第 天,有持股,交易次数为0时,我们手上的最大利润,这里直接套用状态转移方程即可。
4、确定遍历顺序
-
第一层遍历是 天数,从第 天遍历到第 天
-
第二层遍历是 交易次数,但是这个有个小小 ,就是实际天数与交易次数的比较。有 个交易日,最多能交易的次数为 ,并向下取整,故实际的交易次数应为 。所以第二层遍历是从 到
5、确定最后的返回值
找到第 天,不持有股票(),交易 次中,元素最大的值,即
6、上代码
/**
* 空间复杂度O(N * min(k, N/2))
* 时间复杂度O(N * min(k, N/2))
*/
function maxProfit(k: number, prices: number[]): number {
const len = prices.length;
if (len < 2) return 0;
const maxTradeTimes = Math.min(k, ~~(len / 2)) + 1; // 目的是要从交易0次开始
const dp = Array.from(
{ length: len },
() => [
new Array(maxTradeTimes).fill(0),
new Array(maxTradeTimes).fill(0),
],
);
dp[0][0][0] = 0;
dp[0][1][0] = -prices[0];
for(let x = 1; x < len; x++) {
dp[x][1][0] = Math.max(dp[x - 1][1][0], dp[x - 1][0][0] - prices[x]);
}
for(let z = 1; z < maxTradeTimes; z++) {
dp[0][0][z] = Number.MIN_SAFE_INTEGER;
dp[0][1][z] = Number.MIN_SAFE_INTEGER;
}
for(let x = 1; x < len; x ++) {
for(let z = 1; z < maxTradeTimes; z++) {
dp[x][0][z] = Math.max(dp[x - 1][0][z], dp[x - 1][1][z - 1] + prices[x]);
dp[x][1][z] = Math.max(dp[x - 1][1][z], dp[x - 1][0][z] - prices[x]);
}
}
return Math.max(...dp[len - 1][0]);
}