题目描述:
给定一个长度为 N 的数组,数组中的第 i 个数字表示一个给定股票在第 i天的价格。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
- 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
- 卖出股票后,你无法在第二天买入股票 (即冷冻期为1天)。
输入格式
第一行包含整数 N,表示数组长度。
第二行包含 N个不超过 10000的正整数,表示完整的数组。
输出格式
输出一个整数,表示最大利润。
数据范围
1≤N≤10^5
输入样例:
5
1 2 3 0 2
输出样例:
3
样例解释
对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出],第一笔交易可得利润 2-1 = 1,第二笔交易可得利润 2-0 = 2,共得利润 1+2 = 3。
分析:
首先按照普通线性DP的方法做,第i天的状态可以划分为持有股票和未持有股票,f[i][0]表示第i天未持有股票的最大收益,f[i][1]表示持有股票的最大收益。第i天未持有股票可能是第i - 1天就没持有股票,也可能是第i天卖了,故f[i][0] = max(f[i-1][0],f[i-1][1]+w[i])。第i天持有股票可能是第i-1天就持有股票,也可能是第i天买的股票,要想第i天买股票,则第i-2天必不能持股,所以 f[i][1] = max(f[i-1][1],f[i-2][0] - w[i]),边界状态是f[1][0] = 0,f[1][1] = -w[1]。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100005;
int f[N][2];
int main(){
int n,price;
scanf("%d%d",&n,&price);
f[1][1] = -price;
for(int i = 2;i <= n;i++){
scanf("%d",&price);
f[i][0]= max(f[i-1][0],f[i-1][1]+price);
f[i][1] = max(f[i-1][1],f[i-2][0]-price);
}
printf("%d\n",f[n][0]);
return 0;
}
然后按照状态机的方法做,本质也是先考虑状态的划分,上一个方法我们是按第i天有没有持仓进行划分的,对未持仓进行进一步划分,分为第i天刚卖出和第i天没操作没仓位。持仓的状态含义不变。
其实状态2还可以进一步细分为还在冷冻期,即前一天才卖出,以及不在冷冻期可买入的状态,但是不论是哪个,状态2在下一天都可以转化为状态0,所以无须细分。初始状态是状态2,然后第一天可以选择继续无操作还是状态2,也可以选择买入股票转化为状态0。状态0的下一天可以继续维持状态0,也可以卖出转化为状态1,状态1的下一天不能买入,只能选择无操作继续空仓。从而得到状态转移方程为f[i][0] = max(f[i-1][0],f[i-1][2]-w[i]),f[i][1] = f[i-1][0] + w[i],f[i][2] = max(f[i-1][2],f[i-1][1])。边界状态为f[0][0] = f[0][1] = -INF表示不合法的方案,f[0][2]=0表示初始状态。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100005;
int f[N][3];
int main(){
int n,price;
scanf("%d",&n);
f[0][0] = f[0][1] = -1e9;
for(int i = 1;i <= n;i++){
scanf("%d",&price);
f[i][0] = max(f[i-1][0],f[i-1][2] - price);
f[i][1] = f[i-1][0] + price;
f[i][2]= max(f[i-1][2],f[i-1][1]);
}
printf("%d\n",max(f[n][1],f[n][2]));
return 0;
}