题解:学而思编程 股票买卖
【题目来源】
学而思编程:股票买卖
【题目描述】
给定一支股票接下来 \(n\) 天的价格,设计一个算法计算出最大的利润。
每天你可以进行买入或者卖出,或者不进行任何操作,但是需要遵守以下条件:
- 你不能同时参与多笔交易(你必须在再次买入前卖出之前的股票)。
- 卖出股票后,你无法在第二天买入股票(即冷冻期为 \(1\) 天)。
【输入】
第一行一个整数 \(n(1≤n≤10^5)\),表示一共有 \(n\) 天。
第二行一共有 \(n\) 个整数 \(a_i(1≤a_i≤10^9)\),表示第 \(i\) 天的价格。
【输出】
输出一行,包含一个整数,表示最大的利润。
【输入样例】
5
2 3 4 1 3
【输出样例】
3
【核心思想】
-
问题分析:给定 \(n\) 天的股票价格 \(a_i\),每天可以买入、卖出或不操作,但不能同时持有多只股票(再次买入前必须卖出),且卖出后有 \(1\) 天冷冻期(第二天不能买入)。求最大利润。这是一个状态机 DP 问题,需要设计多个状态表示不同的持仓和冷冻情况。
-
算法选择:
- 状态机 DP(线性 DP):定义多个状态表示不同的交易状态
- 状态设计:
- 状态 \(0\):手中没有股票,且不在冷冻期(可以买入)
- 状态 \(1\):手中持有股票
- 状态 \(2\):手中没有股票,但处于冷冻期(刚卖出,不能买入)
-
关键步骤:
- 初始化:\(dp[0][0] = 0\)(第 \(0\) 天没有股票,利润为 \(0\)),其他状态初始化为负无穷
- 状态转移:
- \(dp[i][0] = \max(dp[i-1][0], dp[i-1][2])\):状态 \(0\) 可以从状态 \(0\) 不操作,或从状态 \(2\) 冷冻期结束转移
- \(dp[i][1] = \max(dp[i-1][1], dp[i-1][0] - a[i])\):状态 \(1\) 可以从状态 \(1\) 继续持有,或从状态 \(0\) 买入(花费 \(a[i]\))
- \(dp[i][2] = dp[i-1][1] + a[i]\):状态 \(2\) 只能从状态 \(1\) 卖出股票转移(获得 \(a[i]\))
- 答案:\(\max(dp[n][0], dp[n][2])\),最后不能持有股票
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),一次遍历完成状态转移
- 空间复杂度:\(O(n)\),DP 数组存储,可优化至 \(O(1)\)
-
状态机 DP 的核心思想:
- 状态设计:根据题目约束设计合理的状态,本题用三个状态表示持仓和冷冻情况
- 状态转移:明确每个状态可以从哪些状态转移而来,确保状态转移的完备性
- 最优子结构:当前最优解由子问题的最优解组合而成
- 空间优化:由于只依赖前一天的状态,可以用滚动数组优化至 \(O(1)\)
- 适用于有复杂约束的序列决策问题,如股票交易、任务调度等
【算法标签】
线性DP-一维
【代码详解】
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
const int N = 1e5 + 5;
int n, a[N], dp[N][5]; // dp[i][j]: 前i天,状态j的最大收益
signed main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i]; // 第i天的股价
}
memset(dp, -0x3f, sizeof(dp)); // 初始化为负无穷
dp[0][0] = 0; // 第0天,没有股票
for (int i = 1; i <= n; i++)
{
// 状态0: 没有股票,不操作
dp[i][0] = max(dp[i - 1][0], dp[i - 1][2]);
// 状态1: 持有股票
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - a[i]);
// 状态2: 卖出股票后的冷冻期
dp[i][2] = dp[i - 1][1] + a[i];
}
cout << max(dp[n][0], dp[n][2]); // 最后不能持有股票
return 0;
}
【运行结果】
5
2 3 4 1 3
3
浙公网安备 33010602011771号