问题描述
小R近期表现出色,公司决定以股票的形式给予奖励,并允许他在市场上进行交易以最大化收益。给定一个数组,数组中的第 i 个元素代表第 i 天的股票价格。小R需要设计一个算法来实现最大利润。 股票交易规则如下: • 小R可以多次买卖股票,但在买入新的股票前必须卖出之前的股票。 • 每次卖出股票后存在一天的冷冻期,在冷冻期内小R不能购买股票。 你的任务是帮助小R计算出在遵守交易规则的情况下能够获得的最大利润。 • stocks: 一个整数列表,表示连续几天内的股票价格。
问题理解
详细解释
初始检查:
这段代码检查输入的 stocks 数组是否为空或长度为零。如果是,直接返回 0,因为没有任何股票可以交易。
if (stocks == null || stocks.length == 0) {
return 0;
}
定义状态数组:
- ``hold[i]:表示第 i 天持有股票时的最大利润。 - unhold[i]:表示第 i` 天不持有股票时的最大利润。
int n = stocks.length;
int[] hold = new int[n];
int[] unhold = new int[n];
初始化状态:
for (int i = 1; i < n; i++) {
hold[i] = Math.max(hold[i-1], (i >= 2 ? unhold[i-2] : 0) - stocks[i]);
unhold[i] = Math.max(unhold[i-1], hold[i-1] + stocks[i]);
}
hold[0] = -stocks[0]:第一天买入股票,利润为负的股票价格。unhold[0] = 0:第一天不持有股票,利润为0。
状态转移方程:
-
hold[i] = Math.max(hold[i-1], (i >= 2 ? unhold[i-2] : 0) - stocks[i]): -
hold[i-1]:表示前一天持有股票的最大利润。 -
(i >= 2 ? unhold[i-2] : 0) - stocks[i]:表示前两天不持有股票的最大利润减去当天的股票价格(因为存在冷冻期)。 -
unhold[i] = Math.max(unhold[i-1], hold[i-1] + stocks[i]): -
unhold[i-1]:表示前一天不持有股票的最大利润。 -
hold[i-1] + stocks[i]:表示前一天持有股票的最大利润加上当天的股票价格。
for (int i = 1; i < n; i++) {
hold[i] = Math.max(hold[i-1], (i >= 2 ? unhold[i-2] : 0) - stocks[i]);
unhold[i] = Math.max(unhold[i-1], hold[i-1] + stocks[i]);
}
- 返回结果:
return unhold[n-1];
最终结果是不持有股票的最大利润,即 `unhold[n-1]`。
总结
- 状态定义:
hold[i]和unhold[i]分别表示第i天持有和不持有股票的最大利润。 - 状态转移:根据前一天的状态和当天的股票价格来更新状态。
- 初始化:第一天持有股票的利润为负的股票价格,不持有股票的利润为
0。 - 最终结果:返回最后一天不持有股票的最大利润。
动态规划
动态规划(Dynamic Programming,简称DP)是一种通过将问题分解为更小的子问题来解决复杂问题的算法技术。它通常用于优化问题,其中问题的解决方案可以通过组合子问题的解决方案来获得。
动态规划的核心思想
- 最优子结构:问题的最优解可以通过其子问题的最优解来构造。这意味着问题的解可以分解为多个子问题的解。
- 重叠子问题:在递归求解过程中,许多子问题是重复的。动态规划通过存储这些子问题的解来避免重复计算,从而提高效率。
动态规划的基本步骤
- 定义状态:确定问题的状态表示。状态通常是一个或多个变量的组合,表示问题的某个特定情况。
- 状态转移方程:找到状态之间的关系,即如何从一个状态转移到另一个状态。这通常通过递推关系式来表示。
- 初始条件:确定初始状态的值,这些值是递推的基础。
- 计算顺序:确定计算状态的顺序,通常是从初始状态开始,逐步计算到最终状态。
- 返回结果:根据问题的要求,返回最终状态的值。
动态规划的常见应用
- 斐波那契数列:通过存储中间结果来避免重复计算。
- 最长公共子序列(LCS) :找到两个序列的最长公共子序列。
- 背包问题:在给定重量限制下,选择物品以最大化价值。
- 最短路径问题:在图中找到从起点到终点的最短路径
总代码
public class Main {
public static int solution(int[] stocks) {
if (stocks == null || stocks.length == 0) {
return 0;
}
int n = stocks.length;
int[] hold = new int[n];
int[] unhold = new int[n];
// 初始化
hold[0] = -stocks[0];
unhold[0] = 0;
for (int i = 1; i < n; i++) {
// 状态转移方程
hold[i] = Math.max(hold[i-1], (i >= 2 ? unhold[i-2] : 0) - stocks[i]);
unhold[i] = Math.max(unhold[i-1], hold[i-1] + stocks[i]);
}
// 最终结果是不持有股票的最大利润
return unhold[n-1];
}
public static void main(String[] args) {
// You can add more test cases here
System.out.println(solution(new int[]{1, 2}) == 1);
System.out.println(solution(new int[]{2, 1}) == 0);
System.out.println(solution(new int[]{1, 2, 3, 0, 2}) == 3);
System.out.println(solution(new int[]{2, 3, 4, 5, 6, 7}) == 5);
System.out.println(solution(new int[]{1, 6, 2, 7, 13, 2, 8}) == 12);
}
}