问题描述
小R近期表现出色,公司决定以股票的形式给予奖励,并允许他在市场上进行交易以最大化收益。给定一个数组,数组中的第 i 个元素代表第 i 天的股票价格。小R需要设计一个算法来实现最大利润。
股票交易规则如下:
- 小R可以多次买卖股票,但在买入新的股票前必须卖出之前的股票。
- 每次卖出股票后存在一天的冷冻期,在冷冻期内小R不能购买股票。
你的任务是帮助小R计算出在遵守交易规则的情况下能够获得的最大利润。
stocks: 一个整数列表,表示连续几天内的股票价格。
测试样例
样例1:
输入:
stocks = [1, 2]
输出:1
样例2:
输入:
stocks = [2, 1]
输出:0
样例3:
输入:
stocks = [1, 2, 3, 0, 2]
输出:3
样例4:
输入:
stocks = [2, 3, 4, 5, 6, 7]
输出:5
样例5:
输入:
stocks = [1, 6, 2, 7, 13, 2, 8]
输出:12
解题思路
动态规划
1、dp 数组的含义
dp[i][j]:第 i 天状态为j,获得的最大利润。
根据题目描述,由于最多只能同时买入(持有)一支股票,并且卖出股票后有冷冻期的限制,设置了四种不同的状态
- dp[i][0]:目前持有一支股票;
- dp[i][1]:目前不持有任何股票(可能在冷冻期,可能不在冷冻期);
- dp[i][2]:当天卖出股票;
- dp[i][3]:目前为冷冻期状态,但冷冻期状态不可持续,只有一天!
2、状态转移方程
1)对于 dp[i][0],有两种可能的情况:
- 操作一:前一天就是持有股票状态(延续),dp[i][0] = dp[i - 1][0]
- 操作二:今天买入了,有两种情况
-
- 前一天是冷冻期,dp[i - 1][3] - stocks[i]
-
- 前一天是保持卖出股票的状态,dp[i - 1][1] - stocks[i]
那么 dp[i][0] = max(dp[i - 1][0], dp[i - 1][3] - stocks[i], dp[i - 1][1] - stocks[i]);
2)对于dp[i][1],有两种可能的情况:
- 操作一:前一天就是不持有股票状态
- 操作二:前一天是冷冻期
dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
3)对于dp[i][2] ,只有一个操作:
昨天一定是持有股票状态,今天卖出股票。
即:dp[i][2] = dp[i - 1][0] + stocks[i];
4)dp[i][3],只有一个操作:
昨天卖出了股票,今天是冷冻期。
dp[i][3] = dp[i - 1][2];
3、细节
可以将第 0 天的情况作为动态规划中的边界条件
dp[0][0] = -stocks[0];
在第 0 天时,如果持有股票,那么只能是在第 0 天买入的,对应负收益 −stocks[0],如果不持有股票,那么收益为零。这样就可以从第 1 天开始,根据上面的状态转移方程进行进行动态规划,直到计算出第 n−1 天的结果。
4、代码实现
public static int solution(int[] stocks) {
if (stocks == null || stocks.length < 2) {
return 0;
}
int len = stocks.length;
int[][] dp = new int[stocks.length][4];
// 初始化
dp[0][0] = -stocks[0];
for (int i = 1; i < stocks.length; i++) {
// dp公式
dp[i][0] = Math.max(Math.max(dp[i - 1][0], dp[i - 1][1] - stocks[i]), dp[i - 1][3] - stocks[i]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][3]);
dp[i][2] = dp[i - 1][0] + stocks[i];
dp[i][3] = dp[i - 1][2];
}
return Math.max(Math.max(dp[len - 1][1], dp[len - 1][2]), dp[len - 1][3]);
}
时间复杂度
时间复杂度:O(n)
空间复杂度:O(n)
学习心得
动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。 所有动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。
- 动态规划问题需要清晰地定义状态和状态转移方程;
- 空间优化是解决动态规划问题时减少资源消耗的有效方法;
- 在实际应用中,需要考虑边界条件,例如本题中的数组长度小于2的情况。
知识点延伸
- 动态规划:这是一种解决决策问题的算法思想,通过将问题分解为更小的子问题来解决原问题。
- 空间优化:在动态规划中,可以使用滚动数组或变量来减少空间复杂度。
- 股票问题系列:在LeetCode等平台上,有许多关于股票买卖的问题,它们都可以用动态规划来解。