文章目录

  • 一、股票买卖系列问题
  • 二、leetcode例题讲解股票买卖系列问题
    • 121. 买卖股票的最佳时机
    • 122. 买卖股票的最佳时机 II
    • 123. 买卖股票的最佳时机 III
    • 188. 买卖股票的最佳时机 IV
    • 309. 最佳买卖股票时机含冷冻期
    • 714. 买卖股票的最佳时机含手续费

动态规划问题还有一系列股票买卖的经典问题。在这里进行汇总学习。

一、股票买卖系列问题

121. 买卖股票的最佳时机:股票只能买卖一次。

122. 买卖股票的最佳时机 II:股票可以多次买卖。

123. 买卖股票的最佳时机 III:股票最多买卖两次。

188. 买卖股票的最佳时机 IV:股票最多买卖k次。

309. 最佳买卖股票时机含冷冻期:买卖多次,卖出有1天的冷冻期。

714. 买卖股票的最佳时机含手续费:买卖多次,每次有手续费。

股票买卖系列问题从买买一次到买卖多次,从最多买卖两次到最多买卖k次,从冷冻期再到手续费涵盖多种情况,通过各种情况的处理可以对动态规划有更充分的认识。(同时有的题目贪心算法求解更为简单也会附上相关解法。)

二、leetcode例题讲解股票买卖系列问题

121. 买卖股票的最佳时机

leetcode题目链接:121. 买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例一:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例二:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

解题思路:

1.确定dp数组(dp table)以及下标的含义

dp[i][0] 表示第i天持有股票所得最大利润。

其实一开始利润是0,那么加入第i天买入股票利润就是 -prices[i], 这是一个负数。

dp[i][1] 表示第i天不持有股票所得最大利润。

“持有”不代表就是当天“买入”!也有可能是昨天就买入了,今天保持持有的状态

2.确定递推公式

如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来:

  1. 第i-1天就持有股票,那么就保持现状,所得利润就是昨天持有股票的所得利润 即:dp[i - 1][0]
  2. 第i天买入股票,所得利润就是买入今天的股票后所得利润即:-prices[i]

那么dp[i][0]应该选所得利润最大的,所以dp[i][0] = max(dp[i - 1][0], -prices[i]);

如果第i天不持有股票即dp[i][1], 也可以由两个状态推出来:

  1. 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得利润 即:dp[i - 1][1]
  2. 第i天卖出股票,所得利润就是按照今天股票佳价格卖出后所得利润即:prices[i] + dp[i - 1][0]

同样dp[i][1]取最大的,dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);

3.dp数组如何初始化

dp[0][0]表示第0天持有股票,此时的持有股票就一定是买入股票了,因为不可能有前一天推出来,所以dp[0][0] = -prices[0];

dp[0][1]表示第0天不持有股票,不持有股票那么利润就是0,所以dp[0][1] = 0;

4.确定遍历顺序

从递推公式可以看出dp[i]都是有dp[i - 1]推导出来的,那么一定是从前向后遍历。

5.举例推导DP数组

省略。

Java实现代码

class Solution {public int maxProfit(int[] prices) {if (prices == null || prices.length == 0) return 0;int length = prices.length;// dp[i][0]代表第i天持有股票的最大收益// dp[i][1]代表第i天不持有股票的最大收益int[][] dp = new int[length][2];int result = 0;dp[0][0] = -prices[0];dp[0][1] = 0;for (int i = 1; i < length; i++) {dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);dp[i][1] = Math.max(dp[i - 1][0] + prices[i], dp[i - 1][1]);}return dp[length - 1][1];}
}

这里介绍一下贪心算法

计算更新前i-1最低价格,计算最大利润。

class Solution {public int maxProfit(int[] prices) {int profit = 0, cost = Integer.MAX_VALUE;// profit 更新最大利润, cost更新前i-1最低价格for(int price : prices){cost = Math.min(cost, price);profit = Math.max(profit, price - cost);}return profit;}
}

122. 买卖股票的最佳时机 II

leetcode题目链接:122. 买卖股票的最佳时机 II

给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例一:

输入: prices = [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

示例二:

输入: prices = [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例三:

输入: prices = [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

解题思路:

1.确定dp数组(dp table)以及下标的含义

dp[i][0] 表示第i天持有股票所得最大利润。

dp[i][1] 表示第i天不持有股票所得最大利润。

2.确定递推公式

如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来

  1. 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
  2. 第i天买入股票,所得现金就是昨天不持有股票的所得现金减去 今天的股票价格 即:dp[i - 1][1] - prices[i]

这里是与上一题唯一不同的地方,就是推导dp[i][0]的时候,第i天买入股票的情况

在上一题中,因为股票全程只能买卖一次,所以如果买入股票,那么第i天持有股票即dp[i][0]一定就是 -prices[i]。

而本题,因为一只股票可以买卖多次,所以当第i天买入股票的时候,所持有的利润可能有之前买卖过的利润。

那么第i天持有股票即dp[i][0],如果是第i天买入股票,所得现金就是昨天不持有股票的所得利润 减去 今天的股票价格 即:dp[i - 1][1] - prices[i]

如果第i天不持有股票即dp[i][1], 还和之前相同,由两个状态推出来:

  1. 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得利润 即:dp[i - 1][1]
  2. 第i天卖出股票,所得利润就是按照今天股票佳价格卖出后所得利润即:prices[i] + dp[i - 1][0]

同样dp[i][1]取最大的,dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);

3.dp数组如何初始化

dp[0][0]表示第0天持有股票,此时的持有股票就一定是买入股票了,因为不可能有前一天推出来,所以dp[0][0] = -prices[0];

dp[0][1]表示第0天不持有股票,不持有股票那么利润就是0,所以dp[0][1] = 0;

4.确定遍历顺序

从递推公式可以看出dp[i]都是有dp[i - 1]推导出来的,那么一定是从前向后遍历。

5.举例推导DP数组

省略。

Java实现代码

class Solution {public int maxProfit(int[] prices) {int[][] dp = new int[prices.length][2];dp[0][0] = - prices[0];dp[0][1] = 0;for(int i = 1; i < prices.length; i++){dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] - prices[i]);dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] + prices[i]);}return dp[prices.length-1][1];}
}

这里再介绍一下贪心算法的实现:

因为交易次数不受限,如果可以把所有的利润全部收集到,一定是利益最大化的。

class Solution {public int maxProfit(int[] prices) {// 1.贪心算法,只要后一天比前一天大,就把差值加一下int res = 0;for(int i = 1; i < prices.length; i++){if(prices[i] > prices[i-1]){res += prices[i] - prices[i-1];}}return res;}
}

123. 买卖股票的最佳时机 III

leetcode题目链接:123. 买卖股票的最佳时机 III

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例一:

输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。

示例二:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。   注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。   因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例三:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这个情况下, 没有交易完成, 所以最大利润为 0。

解题思路:

1.确定dp数组(dp table)以及下标的含义

一天一共就有五个状态,
0.没有操作
1.第一次买入
2.第一次卖出
3.第二次买入
4.第二次卖出

dp[i][j]中 i表示第i天,j为 [0 - 4] 五个状态,dp[i][j]表示第i天状态j所剩最大利润

2.确定递推公式

达到dp[i][1]状态,有两个具体操作:

操作一:第i天买入股票了,那么dp[i][1] = dp[i-1][0] - prices[i]
操作二:第i天没有操作,而是沿用前一天买入的状态,即:dp[i][1] = dp[i - 1][1]

所以 dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]);

dp[i][2]也有两个操作:

操作一:第i天卖出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]
操作二:第i天没有操作,沿用前一天卖出股票的状态,即:dp[i][2] = dp[i - 1][2]

所以dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])

同理可推出剩下状态部分:

dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);

dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);

3.dp数组如何初始化

第0天没有操作,这个最容易想到,就是0,即:dp[0][0] = 0;

第0天做第一次买入的操作,dp[0][1] = -prices[0];

第0天做第一次卖出的操作,这个初始值应该是多少呢?

首先卖出的操作一定是收获利润,整个股票买卖最差情况也就是没有盈利即全程无操作利润为0,

从递推公式中可以看出每次是取最大值,那么既然是收获利润如果比0还小了就没有必要收获这个利润了。

所以dp[0][2] = 0;

第0天第二次买入操作,初始值应该是多少呢?应该不少同学疑惑,第一次还没买入呢,怎么初始化第二次买入呢?

第二次买入依赖于第一次卖出的状态,其实相当于第0天第一次买入了,第一次卖出了,然后在买入一次(第二次买入),那么现在手头上没有利润,只要买入,利润就做相应的减少。

所以第二次买入操作,初始化为:dp[0][3] = -prices[0];

同理第二次卖出初始化dp[0][4] = 0;

4.确定遍历顺序

从递推公式可以看出dp[i]都是有dp[i - 1]推导出来的,那么一定是从前向后遍历。

5.举例推导DP数组

省略。

Java代码实现

class Solution {public int maxProfit(int[] prices) {if(prices.length == 0) return 0;/*定义5种状态0:没有操作, 1.第一次买入,2.第一次卖出,3.第二次买入,4.第二次卖出*/// dp[i][j] 表示第i天状态j获得的最大利润int[][] dp = new int[prices.length][5];dp[0][1] = - prices[0];// 初始化第二次买入的状态是确保结果是最多两次买卖的最大利润dp[0][3] = - prices[0];for(int i = 1; i < prices.length; i++){dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i]);dp[i][2] = Math.max(dp[i-1][2], dp[i-1][1] + prices[i]);dp[i][3] = Math.max(dp[i-1][3], dp[i-1][2] - prices[i]);dp[i][4] = Math.max(dp[i-1][4], dp[i-1][3] + prices[i]);}return dp[prices.length-1][4];}
}

动态规划 + 空间优化

class Solution {public int maxProfit(int[] prices) {if(prices.length == 0) return 0;/*定义5种状态0:没有操作, 1.第一次买入,2.第一次卖出,3.第二次买入,4.第二次卖出*/// 空间优化int[] dp = new int[5];dp[1] = - prices[0];dp[3] = - prices[0];for(int i = 1; i < prices.length; i++){dp[1] = Math.max(dp[1], dp[0] - prices[i]);dp[2] = Math.max(dp[2], dp[1] + prices[i]);dp[3] = Math.max(dp[3], dp[2] - prices[i]);dp[4] = Math.max(dp[4], dp[3] + prices[i]);}return dp[4];}
}

dp[2]利用的是当天的dp[1]。 但结果也是对的。

dp[1] = max(dp[1], dp[0] - prices[i]); 如果dp[1]取dp[1],即保持买入股票的状态,

那么 dp[2] = max(dp[2], dp[1] + prices[i]);中dp[1] + prices[i] 就是今天卖出。

如果dp[1]取dp[0] - prices[i],今天买入股票,那么dp[2] = max(dp[2], dp[1] + prices[i]);中的dp[1] + prices[i]相当于是尽在再卖出股票,一买一卖收益为0,对所得利润没有影响。

相当于今天买入股票又卖出股票,等于没有操作,保持昨天卖出股票的状态了。

188. 买卖股票的最佳时机 IV

leetcode题目链接:188. 买卖股票的最佳时机 IV

给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例一:

入:k = 2, prices = [2,4,1]
输出:2
解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。

示例二:

输入:k = 2, prices = [3,2,6,5,0,3]
输出:7
解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。

解题思路:

1.确定dp数组(dp table)以及下标的含义

二维数组 dp[i][j] :第i天的状态为j,所获得的最大利润是dp[i][j]
j 的状态:
0.没有操作
1.第一次买入
2.第一次卖出
3.第二次买入
4.第二次卖出
5.……

除了0以外,偶数就是卖出,奇数就是买入。

题目要求是至多有K笔交易,那么j的范围就定义为 2 * k + 1 就可以了。

2.确定递推公式

本题和上一题的区别就是这里要类比j为奇数是买,偶数是卖剩的状态。

for(int i = 1; i < prices.length; i++){for(int j = 0; j < 2*k-1; j+=2){dp[i][j+1] = Math.max(dp[i-1][j+1], dp[i-1][j] - prices[i]);dp[i][j+2] = Math.max(dp[i-1][j+2], dp[i-1][j+1] + prices[i]);}
}

3.dp数组如何初始化

dp[0][j]当j为奇数的时候都初始化为 -prices[0]

dp[0][j]当j为偶数的时候都初始化为 0

for(int i = 1; i < 2*k; i+=2){dp[0][i] = -prices[0];
}

4.确定遍历顺序

从递推公式可以看出dp[i]都是有dp[i - 1]推导出来的,那么一定是从前向后遍历。

5.举例推导DP数组

省略。

Java代码实现:

class Solution {public int maxProfit(int k, int[] prices) {if(prices.length == 0) return 0;// dp[i][j] 表示第i天状态j获得的最大利润。/*j的状态0 无操作1 第一次买入2 第一次卖出3 第二次买入4 第二次卖出5 ……至多k次交易,则j的取值范围定义为 2 * k + 1*/// 二维 dp 数组,[天数][股票状态]// 股票状态,奇数表示第k次交易持有/买入,偶数表示不持有/卖出,0无操作int[][] dp = new int[prices.length][2 * k + 1];// 初始化,奇数次,买入则赋值为-prices[0]for(int i = 1; i < 2*k; i+=2){dp[0][i] = -prices[0];}for(int i = 1; i < prices.length; i++){for(int j = 0; j < 2*k-1; j+=2){dp[i][j+1] = Math.max(dp[i-1][j+1], dp[i-1][j] - prices[i]);dp[i][j+2] = Math.max(dp[i-1][j+2], dp[i-1][j+1] + prices[i]);}}return dp[prices.length-1][2*k];}
}

空间优化,一维dp数组

class Solution {public int maxProfit(int k, int[] prices) {if(prices.length == 0) return 0;// 空间优化,一维dp数组int[] dp = new int[2 * k + 1];// 初始化,奇数次,买入则赋值为-prices[0]for(int i = 1; i < 2*k; i+=2){dp[i] = -prices[0];}for(int i = 1; i < prices.length; i++){for(int j = 0; j < 2*k-1; j+=2){dp[j+1] = Math.max(dp[j+1], dp[j] - prices[i]);dp[j+2] = Math.max(dp[j+2], dp[j+1] + prices[i]);}}return dp[2*k];}
}

309. 最佳买卖股票时机含冷冻期

leetcode题目链接:309. 最佳买卖股票时机含冷冻期

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

示例一:

输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

解题思路:

相对于122. 买卖股票的最佳时机 II,本题加上了一个冷冻期。冷冻期什么也不能干,冷冻期结束转为不持股状态。

1.确定dp数组(dp table)以及下标的含义

二维数组 dp[i][0] :第i天不持股所获得的最大利润

dp[i][1] :第i天持股所获得的最大利润

dp[i][2] :第i天冷冻期所获得的最大利润

这里把 0 和 1的状态和Ⅱ的弄反了,不过不影响,自己理清楚就行。

2.确定递推公式

第 i 天不持股可以从两种状态转移,:

  1. 第 i-1 天不持股,第 i 天仍不买 dp[i][0] = dp[i-1][0];
  2. 冷冻期结束了,但不买 dp[i][0] = dp[i-1][2];

所以 dp[i][0] = Math.max(dp[i-1][0], dp[i-1][2]);

第 i 天持股可以从两种状态转移,

  1. 第 i-1 天不持股,分为 i-1 天是冷冻期,结束后转为不持股状态和第 i-1 天本身就不持股,然后买入 dp[i][1] = dp[i-1][0] - prices[i]
  2. 第 i-1 天持股,第 i 天不卖出 dp[i][1] = dp[i-1][1]

所以:dp[i][1] = Math.max(dp[i-1][0] - prices[i], dp[i-1][1]);

第 i 天处于冷冻期的状态:

  1. 第 i-1 天卖出

所以:dp[i][2] = dp[i-1][1] + prices[i];

只有最后一天不持股或者前一天已经卖掉(今天为冷冻期),最大值在二者之间产生

3.dp数组如何初始化

dp[0][1]当持股的时候都初始化为 -prices[0]

dp[0][0]不持股和dp[0][2]冷冻期都初始化为 0

4.确定遍历顺序

从递推公式可以看出dp[i]都是有dp[i - 1]推导出来的,那么一定是从前向后遍历。

5.举例推导DP数组

省略。

Java代码实现:

class Solution {public int maxProfit(int[] prices) {// 动态规划,相比2多了一个冷冻期,冷冻期什么也不能干,冷冻期结束转为不持股状态if(prices.length == 0) return 0;int[][] dp = new int[prices.length][3]; // 0 不持股,1 持股,2 冷冻期dp[0][1] = - prices[0]; // 持股for(int i = 1; i < prices.length; i++){// 第i天不持股可以从两种状态转移, 1. 第i-1天不持股,第i天仍不买  2.冷冻期结束了,但不买dp[i][0] = Math.max(dp[i-1][0], dp[i-1][2]);// 第i天持股可以从两种状态转移, 1. 第i-1天不持股,分为i-1天是冷冻期,结束后转为不持股状态和第i-1天本身就不持股,然后买// 2.第i-1天持股,第i天不卖出dp[i][1] = Math.max(dp[i-1][0] - prices[i], dp[i-1][1]);// 只有第i-1天卖出,第i天才处于冷冻期dp[i][2] = dp[i-1][1] + prices[i];}// 只有最后一天不持股或者前一天已经卖掉(今天为冷冻期),最大值在二者之间产生return Math.max(dp[prices.length-1][0], dp[prices.length-1][2]);}
}

714. 买卖股票的最佳时机含手续费

leetcode题目链接:714. 买卖股票的最佳时机含手续费

给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

示例一:

输入:prices = [1, 3, 2, 8, 4, 9], fee = 2
输出:8
解释:能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8

示例二:

输入:prices = [1,3,7,5,10,3], fee = 3
输出:6

解题思路:

相对于122. 买卖股票的最佳时机 II,本题只需要在计算买入(也可以选择卖出)操作的时候减去手续费就可以了,代码几乎是一样的。

1.确定dp数组(dp table)以及下标的含义

二维数组 :

dp[i][0] :第i天不持股所获得的最大利润

dp[i][1] :第i天持股所获得的最大利润

这里把 0 和 1的状态和Ⅱ的弄反了,不过不影响,自己理清楚就行。

2.确定递推公式

如果第i天不持有股票即dp[i][0]的情况, 可以由两个状态推出来:

  1. 第i-1天就不持有股票,那么就保持现状,所得利润就是昨天不持有股票的所得利润 即:dp[i - 1][0]
  2. 第i天卖出股票,所得利润就是按照今天股票价格卖出后所得利润,即:dp[i - 1][1] + prices[i]

所以:dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i]);

如果第i天持有股票即dp[i][1], 那么可以由两个状态推出来:

  1. 第i-1天就持有股票,那么就保持现状,所得利润就是昨天持有股票的所得利润 即:dp[i - 1][1]
  2. 第i天买入股票,所得利润就是昨天不持有股票的所得利润减去今天的股票价格,注意这里需要有手续费了即:dp[i - 1][0] - prices[i] - fee

所以:dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i] - fee);

3.dp数组如何初始化

dp[0][0] 当不持股的时候都初始化为 0

dp[0][1] 当持股的时候都初始化为 -prices[0] - fee

4.确定遍历顺序

从递推公式可以看出dp[i]都是有dp[i - 1]推导出来的,那么一定是从前向后遍历。

5.举例推导DP数组

省略。

Java代码实现:

class Solution {public int maxProfit(int[] prices, int fee) {// 动态规划,含手续费的股票交易int[][] dp = new int[prices.length][2];dp[0][0] = 0;  // 不持股dp[0][1] = - prices[0] - fee; // 持股for(int i = 1; i < prices.length; i++){dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i]);dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i] - fee);}return dp[prices.length-1][0];}
}

空间优化,减少状态变量

class Solution {public int maxProfit(int[] prices, int fee) {// 动态规划,减少状态变量int a = 0;  // 表示今日手里   没有股票的最大收益。int b = Integer.MIN_VALUE;  // 表示今日手里   有股票的的最大收益。for (int price : prices) {// 没有股票,可能昨天就没有,或者昨天有,今天出手了。a = Math.max(a, b + price);// 有股票,可能昨天就有,或者今天刚买的。b = Math.max(b, a - price - fee);}return a;}
}

贪心算法:

class Solution {public int maxProfit(int[] prices, int fee) {// 贪心算法,int n = prices.length;int buy = prices[0] + fee;int profit = 0;for (int i = 1; i < n; ++i) {if (prices[i] + fee < buy) {buy = prices[i] + fee;} else if (prices[i] > buy) {profit += prices[i] - buy;buy = prices[i];}}return profit;}
}

以上就是动态规划股票买卖的一系列题目。问题逐步变化,需要理清的主要是状态的设置。有的题两个状态,有的题多个状态。

动态规划其它题型总结:

动态规划之背包问题——01背包

动态规划之背包问题——完全背包

动态规划之打家劫舍系列问题

动态规划之子序列问题

动态规划之股票买卖系列问题相关推荐

  1. 【leetcode】股票买卖系列总结

    股票买卖系列总结 股票买卖系列的题目在面试中还是比较经典的,这里对这一系列做一些简单的总结. 1. 只允许买卖一次 假设股票价格序列为(3, 5, 7, 3, 8, 1) 动态规划.整个过程中的行为选 ...

  2. 【LeetCode股票买卖系列:714. 买卖股票的最佳时机含手续费 | 暴力递归=>记忆化搜索=>动态规划】

  3. 这篇能让你搞懂股票买卖系列问题

    写在前面 如果觉得写的还不错,有所收获,记得点个关注和点个赞哟,不胜感激. 股票买卖的这个系列问题早在之前就在LeetCode就刷了一遍了,不过当时因为一些原因,没有总结成一篇博文.后面又陆陆续续遇到 ...

  4. 【leetcode】【动态规划】股票买卖

    leetcode 股票买卖(动态规划) 这位大佬四种题型总结的很好:link 一共只有三种状态:买.卖.冷冻 buy[i]buy[i]buy[i] 表示第i天之前最后一次行为是buy时,最大的收益 s ...

  5. 破解大厂最难算法面试题:动态规划之股票买卖收益最大化

    最近有个猎头突然给我推荐一份工作,面试流程是先在网上做几道测试题.我突然发现这类网上测试有一个规律,如果面试的是外企那么通常在HakerRank上做题,例如亚马逊,如果面试的是国内企业,例如华为等,通 ...

  6. 动态规划之背包问题系列(一)

    问题描述: 给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高. 分类: 有 N 件物品和一个容量是 V 的背包.每件物品只能使用一次. 第 i件物品 ...

  7. 动态规划之状态机模型

    状态机模型 状态机 状态机与动态规划之间的联系 状态机的本质之动态规划描述 股票买卖系列 股票买卖 股票买卖Ⅱ 一.动态规划解法 二.贪心解法 股票买卖Ⅲ 前后缀分解 股票买卖Ⅳ 股票买卖Ⅴ KMP ...

  8. 动态规划之背包问题——01背包

    算法相关数据结构总结: 序号 数据结构 文章 1 动态规划 动态规划之背包问题--01背包 动态规划之背包问题--完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 ...

  9. 算法(Java)——动态规划

    算法相关数据结构总结: 序号 数据结构 文章 1 动态规划 动态规划之背包问题--01背包 动态规划之背包问题--完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 ...

最新文章

  1. [Voice communications] 让音乐响起来
  2. ASP.NET中常用到的27个函数集
  3. VBS脚本常用经典代码收集
  4. 指定jdk8_动图+源码+总结:深度解析 JDK8 中的数据结构(珍藏版)
  5. 在Global Object Services (GOS) 中加入自定义项目
  6. 本地工程提交github
  7. Linux 退出保存/不保存
  8. opencv中的Mat类型
  9. 【OS学习笔记】二十 保护模式六:保户模式下操作系统内核如何加载用户程序并运行 对应的汇编代码之主引导扇区程序
  10. 静态html js文件上传,js实现动态添加上传文件页面
  11. qt中 accept()和ignore()函数
  12. xml生成java代码_在Eclipse中从XML生成Java代码
  13. [转载] Python:Numpy详解
  14. android中handler简单用法
  15. Atitit.并发编程原理与概论 attilax总结
  16. 【Protel】Protel99SE(附汉化包+SP6+增强工具+视频教程)
  17. 防火电缆分类、标准、阻燃等级划分详细说明
  18. 2017计算机应用基础实践,计算机应用基础试题及答案
  19. hitb2017 sentosa writeup
  20. 表达式计算器-iExpr

热门文章

  1. 高级计量经济学(part1)--什么是计量经济学
  2. Python实现局域网IP端口扫描
  3. USB电路接口线序及故障排除
  4. 微信登录实现-Android
  5. RocketMQ过滤器demo实现--李波RocketMQ学习
  6. 手把手教你实现一个 Python 计时器
  7. 电压电流采集模块,温湿度采集,称重模块,变送器,adc模数转换模块
  8. 软件测试性能测试安全测试
  9. 39、基于51单片机声控光控灯人体感应路灯照明灯系统设计
  10. java 循环依赖_Spring循环依赖的三种方式