题目描述
给定一个长度为 N N N 的数组,数组中的第 i i i 个数字表示一个给定股票在第 i i i 天的价格。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
输入格式
第一行包含整数 N N N,表示数组长度。
第二行包含 N N N 个不超过 10000 的正整数,表示完整的数组。
输出格式
输出一个整数,表示最大利润。
数据范围
1 ≤ N ≤ 1 0 5 1≤N≤10^5 1≤N≤105
输入样例
5
1 2 3 0 2
输出样例
3
样例解释
对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出],第一笔交易可得利润 2-1 = 1,第二笔交易可得利润 2-0 = 2,共得利润 1+2 = 3。
算法思想
根据题目描述,在 N N N天里不限制交易次数,但是必须满足
- 不能同时参与多笔交易
- 卖出股票后,无法在第二天买入股票 。
如果将每一天作为一个阶段进行处理,对每一个阶段来说,可以进行股票交易、买入卖出,也可能是冷冻期、无法买入卖出。因此可以借助状态机的思想,将每天的状态细分为冷冻期外不持有股票(空仓)、冷冻期内不持有股票(冷冻期),手中持有股票(持仓) 3种状态,如下图所示:
上述状态机描述了5种符合条件的状态转换:
- 没有股票(空仓)-> 没有股票(空仓),继续空仓
- 没有股票(冷冻期)-> 没有股票(空仓),等待
- 持有股票(持仓)-> 持有股票(持仓),继续持有
- 没有股票(空仓)-> 持有股票(持仓),买入
- 持有股票(持仓)-> 没有股票(冷冻期),清仓卖出
因此,可以使用该状态机模型定义每个阶段的状态,并进行状态计算。
状态表示
f[i][0]
表示在第i
天空仓(不在冷冻期、不持有股票)时,前i
天的最大收益f[i][1]
表示第i
天为冷冻期(不持有股票)时,前i
天的最大收益f[i][2]
表示在第i
天持仓(持有股票)时,前i
天的最大收益
状态计算
f[i][0]
取第i - 1
天空仓、或者第i - 1
天为冷冻期时,前i - 1
天的最大收益:f[i][0] = max(f[i - 1][0], f[i - 1][1])
f[i][1]
取第i - 1
天卖出股票时,前i - 1
天的最大收益 + 第i
天卖出股票的收益:f[i][1] = f[i - 1][2] + w[i]
f[i][2]
取第i - 1
天持仓、或者在第i - 1
天买入股票时的前i - 1
天的最大收益减去卖出股票时的收益:f[i][2] = max(f[i - 1][2], f[i - 1][0] - w[i])
初始状态
f[0][0] = 0
表示最初不进行任何交易时空仓的最大收益为0
。
f[0][1] = f[0][2] = -INF
表示f[0][1]
、f[0][2]
都是不合法状态。
时间复杂度
状态数:
n
×
3
n \times 3
n×3
转移计算:
O
(
1
)
O(1)
O(1)
时间复杂度:
O
(
n
×
3
)
O(n \times 3)
O(n×3)
代码实现
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 100010, INF = 0x3f3f3f3f;
//f[i][0]表示在第i天空仓(不在冷冻期、不持有股票)时,前i天的最大收益
//f[i][1]表示第i天为冷冻期(不持有股票)时,前i天的最大收益
//f[i][2]表示在第i天持仓(持有股票)时,前i天的最大收益
int w[N], f[N][3];
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", &w[i]);
//初始状态
//f[0][0]表示最初不进行任何交易时空仓的最大收益为0
//f[0][1]、f[0][2]都是不合法状态
f[0][0] = 0, f[0][1] = f[0][2] = -INF;
for(int i = 1; i <= n; i ++)
{
//f[i][0]取第i - 1天空仓、或者第i - 1天为冷冻期时,前i - 1天的最大收益
f[i][0] = max(f[i - 1][0], f[i - 1][1]);
//f[i][1]取第i - 1天卖出股票时,前i - 1天的最大收益 + 第i天卖出股票的收益
f[i][1] = f[i - 1][2] + w[i];
//f[i][2]取第i - 1天持仓,或者第i - 1天买入股票时的前i - 1天的最大收益
f[i][2] = max(f[i - 1][2], f[i - 1][0] - w[i]);
}
printf("%d\n", max(f[n][0], f[n][1]));
return 0;
}