LeetCode股票买卖问题通用解法

问题描述

给定一个数组,它的第 i i i 个元素为一支给定的股票在第 i i i 天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成 k k k 笔交易。

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

示例 1:

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

示例 2:

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

解析

这道题可以用动态规划的思想来解决。
每到新的一天,有三种选择:购买股票(在没有持有股票的情况下),不买也不卖卖出手中的股票(在持有股票的前提下)。同一天不能既卖出股票又买入股票。

此外,还有购买次数 k k k 的限制,在超出购买次数后,只能在持有股票的前提下,将手中的股票卖出,或者继续观望

按照上述规律我们可以定义:
状态方程 d p [ i ] [ k ] [ 0 ] dp[i][k][0] dp[i][k][0] 的含义为:在第 i i i 天中,已经消耗了 k k k 次股票购买机会,并且没有持有股票。
状态方程 d p [ i ] [ k ] [ 1 ] dp[i][k][1] dp[i][k][1] 的含义为:在第 i i i 天中,已经消耗了 k k k 次股票购买机会,并且当前持有股票。
例如 d p [ 2 ] [ 1 ] [ 0 ] dp[2][1][0] dp[2][1][0] 的含义就是在第2天中,已经消耗了1次股票购买机会,并且当前没有持有股票。

状态方程 d p [ i ] [ k ] [ 0 ] dp[i][k][0] dp[i][k][0] 或者 d p [ i ] [ k ] [ 1 ] dp[i][k][1] dp[i][k][1] 的值定义为当前的收益(可以为正也可以为负),当购买股票时,需要减去 p r i c e s [ i ] prices[i] prices[i],抛售股票时需要加上 p r i c e s [ i ] prices[i] prices[i]。可以看出,题目的最优解为 m a x ( d p [ i ] [ 0 ] [ 0 ] , d p [ i ] [ 1 ] [ 0 ] , . . . , d p [ i ] [ k ] [ 0 ] ) max(dp[i][0][0],dp[i][1][0],...,dp[i][k][0]) max(dp[i][0][0],dp[i][1][0],...,dp[i][k][0]),因为我们并不需要一定把购买机会 k k k 用完。

可以总结出状态转移方程:
d p [ i ] [ k ] [ 0 ] = m a x ( d p [ i − 1 ] [ k ] [ 0 ] , d p [ i − 1 ] [ k ] [ 1 ] + p r i c e s [ i ] ) dp[i][k][0]=max(dp[i-1][k][0],dp[i-1][k][1]+prices[i]) dp[i][k][0]=max(dp[i1][k][0],dp[i1][k][1]+prices[i])
解释: d p [ i ] [ k ] [ 0 ] dp[i][k][0] dp[i][k][0] 代表今天我没有持有股票,那么也就有两种可能性,第一种可能性是昨天没买也没有持有股票。第二种可能性是昨天已经持有了股票,然后今天将股票抛售出去(这里定义抛售不会改变 k k k 的值,只有购买才会将 k k k 的值加上1)。

d p [ i ] [ k ] [ 1 ] = m a x ( d p [ i − 1 ] [ k ] [ 1 ] , d p [ i − 1 ] [ k − 1 ] [ 0 ] − p r i c e s [ i ] ) dp[i][k][1]=max(dp[i-1][k][1],dp[i-1][k-1][0]-prices[i]) dp[i][k][1]=max(dp[i1][k][1],dp[i1][k1][0]prices[i])
解释: d p [ i ] [ k ] [ 1 ] dp[i][k][1] dp[i][k][1] 代表今天我持有股票,也有两种可能性:第一种是昨天就已经持有了股票。第二种可能性就是昨天没有持有股票,然后今天我购买了这笔股票,所以要减去 p r i c e s [ i ] prices[i] prices[i]

状态转移方程定义好后,我们需要定义好初始状态,也就是第一天的所有状态:

  • d p [ 0 ] [ 0 ] [ 0 ] = 0 dp[0][0][0]=0 dp[0][0][0]=0,含义是第1天不买任何股票。
  • d p [ 0 ] [ 1 ] [ 1 ] = − p r i c e [ 0 ] dp[0][1][1] =-price[0] dp[0][1][1]=price[0],含义是第1天买下当天的股票。
  • 第1天除去上述两个状态以外的所有状态都赋值为 − ∞ -\infty (因为第一天只有上述两种可能),在代码中可以表示为Integer.MIN_VALUE >> 1,这里除以2是为了防止在减法运算时超出范围变为正数

计算初始状态的代码为:

for (int i = 0; i <= k; i++) {dp[0][i][0] = Integer.MIN_VALUE >> 1;dp[0][i][1] = Integer.MIN_VALUE >> 1;
}dp[0][0][0] = 0;
dp[0][1][1] = -prices[0];

接下来就是计算状态,根据状态转移方程可以看出,每天的状态都依赖于前一天的状态,所以可以通过两层循环,外层循环从第2天开始遍历(因为第一天的状态已经定义好),然后内层循环从 1 遍历到 k k k

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

为什么不计算 j = 0 j =0 j=0 的状态呢,因为 j = 0 j=0 j=0 的状态意味着没有购买也没有售出任何一支股票,自然也就等于0了,而在Java语言中,数组初始化后初始值为0,此外当 j = 0 j=0 j=0 d p [ i ] [ j ] [ 1 ] dp[i][j][1] dp[i][j][1] 这种不可能的状态也没必要计算,因为在上述代码中 d p [ i ] [ j ] [ 1 ] dp[i][j][1] dp[i][j][1]只依赖于 d p [ i − 1 ] [ j − 1 ] [ 0 ] dp[i-1][j-1][0] dp[i1][j1][0]

完成上述状态的计算后,最后就是求 m a x ( d p [ i ] [ 0 ] [ 0 ] , d p [ i ] [ 1 ] [ 0 ] , . . . , d p [ i ] [ k ] [ 0 ] ) max(dp[i][0][0],dp[i][1][0],...,dp[i][k][0]) max(dp[i][0][0],dp[i][1][0],...,dp[i][k][0]) 也就是题目的答案了。

int ans = 0;
for (int i = 0; i <= k; i++) {ans = Math.max(ans, dp[day - 1][i][0]);
}

分析完成后,我们来看看如何利用上述方程解6道关于股票买卖问题的LeetCode算法题。


LeetCode 121:买卖股票的最佳时机

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

如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

注意你不能在买入股票前卖出股票。

示例 1:

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

示例 2:

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

分析

这道题实际上就是上述原型中的 k = 1 k=1 k=1 的例子。也可以直接套在上述解题模板中,但是之前我们提到过, k = 0 k=0 k=0 的状态意味着没有购买也没有售出任何一支股票,所以恒为0,所以在本题中原先的状态转移方程可以改写为:

d p [ i ] [ k ] [ 0 ] = m a x ( d p [ i − 1 ] [ k ] [ 0 ] , d p [ i − 1 ] [ k ] [ 1 ] + p r i c e s [ i ] ) dp[i][k][0]=max(dp[i-1][k][0],dp[i-1][k][1]+prices[i]) dp[i][k][0]=max(dp[i1][k][0],dp[i1][k][1]+prices[i])
d p [ i ] [ k ] [ 1 ] = m a x ( d p [ i − 1 ] [ k ] [ 1 ] , − p r i c e s [ i ] ) dp[i][k][1]=max(dp[i-1][k][1],-prices[i]) dp[i][k][1]=max(dp[i1][k][1],prices[i])

可以看出,因为 d p [ 0 ] [ 0 ] [ 0 ] , d p [ 1 ] [ 0 ] [ 0 ] , . . . , d p [ n ] [ 0 ] [ 0 ] = 0 dp[0][0][0],dp[1][0][0],...,dp[n][0][0]=0 dp[0][0][0],dp[1][0][0],...,dp[n][0][0]=0,并且也只有 d p [ i ] [ k ] [ 1 ] dp[i][k][1] dp[i][k][1]依赖于 k k k的前一个状态 d p [ i ] [ k − 1 ] [ 0 ] dp[i][k-1][0] dp[i][k1][0],而 k k k 的最大值为1,所以 d p [ i ] [ k − 1 ] [ 0 ] = 0 dp[i][k-1][0] =0 dp[i][k1][0]=0,也就可以省略 d p [ i ] [ k − 1 ] [ 0 ] dp[i][k-1][0] dp[i][k1][0] 了,也就变成了 − p r i c e s [ i ] -prices[i] prices[i]

上述状态转移方程还可以简化,就是去除状态 k k k,因为上述状态转移方程中 k k k 已经不依赖于 k − 1 k-1 k1了,所以该维度可以除去,最终变为:

d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]) dp[i][0]=max(dp[i1][0],dp[i1][1]+prices[i])
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , − p r i c e s [ i ] ) dp[i][1]=max(dp[i-1][1],-prices[i]) dp[i][1]=max(dp[i1][1],prices[i])

题目最后的答案为:

class Solution {public int maxProfit(int[] prices) {int len = prices.length;if(len == 0) {return 0;}int[][] dp = new int[len][2];dp[0][0] = 0;dp[0][1] = -prices[0];for (int i = 1; i < len; 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], -prices[i]);}return dp[len - 1][0];}
}

其时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)

当然这道题由于情况比较简单,还有更省空间、快速的解法。因为只能购买一次,所以我们只需要找出第 i i i 天之后的最大值就可以了,也就是找到数组pricei + 1 ~ len的最大值。我们定义 d p [ i ] dp[i] dp[i] 为数组pricei + 1 ~ len的最大值,可以得到 d p [ i ] dp[i] dp[i] 的状态转移方程为:

d p [ i ] = m a x ( d p [ i + 1 ] , p r i c e s [ i ] ) dp[i]=max(dp[i+1],prices[i]) dp[i]=max(dp[i+1],prices[i])

显然我们只需要找出 d p [ i ] − p r i c e s [ i ] dp[i]-prices[i] dp[i]prices[i]的最大值就可以了,也就是

m a x ( d p [ 0 ] − p r i c e s [ 0 ] , d p [ 1 ] − p r i c e s [ 1 ] , . . . , d p [ n ] − p r i c e s [ n ] ) max(dp[0]-prices[0],dp[1]-prices[1],...,dp[n]-prices[n]) max(dp[0]prices[0],dp[1]prices[1],...,dp[n]prices[n])

最终代码:

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

其时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)

LeetCode 122:买卖股票的最佳时机II

给定一个数组,它的第 i i i 个元素是一支给定股票第 i i i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

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

示例 2:

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

示例 3:

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

分析

这种是属于 k = + ∞ k=+\infty k=+ 的情况,因为 + ∞ +\infty + 无论加1减去1都等于 + ∞ +\infty +,可以认定 k k k k − 1 k-1 k1 是等价的,可以根据这个推导出状态转移方程:

d p [ i ] [ k ] [ 0 ] = m a x ( d p [ i − 1 ] [ k ] [ 0 ] , d p [ i − 1 ] [ k ] [ 1 ] + p r i c e s [ i ] ) dp[i][k][0]=max(dp[i-1][k][0],dp[i-1][k][1]+prices[i]) dp[i][k][0]=max(dp[i1][k][0],dp[i1][k][1]+prices[i])
d p [ i ] [ k ] [ 1 ] = m a x ( d p [ i − 1 ] [ k ] [ 1 ] , d p [ i ] [ k − 1 ] [ 0 ] − p r i c e s [ i ] ) = m a x ( d p [ i − 1 ] [ k ] [ 1 ] , d p [ i ] [ k ] [ 0 ] − p r i c e s [ i ] ) dp[i][k][1]=max(dp[i-1][k][1],dp[i][k-1][0]-prices[i])=max(dp[i-1][k][1],dp[i][k][0]-prices[i]) dp[i][k][1]=max(dp[i1][k][1],dp[i][k1][0]prices[i])=max(dp[i1][k][1],dp[i][k][0]prices[i])

上述状态转移方程中 k k k 已经不依赖于 k − 1 k-1 k1了,所以 k k k 这个维度可以去除,变为:

d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]) dp[i][0]=max(dp[i1][0],dp[i1][1]+prices[i])
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i ] [ 0 ] − p r i c e s [ i ] ) dp[i][1]=max(dp[i-1][1],dp[i][0]-prices[i]) dp[i][1]=max(dp[i1][1],dp[i][0]prices[i])

最终代码:

class Solution {public int maxProfit(int[] prices) {int len = prices.length;if(len == 0) {return 0;}int[][] dp = new int[len][2];dp[0][0] = 0;dp[0][1] = -prices[0];for (int i = 1; i < len; 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][0] - prices[i]);}return dp[len - 1][0];}
}

相比第一个,也就只有dp[i][1] = Math.max(dp[i - 1][1], dp[i][0] - prices[i])这一行发生了改变。时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)

此外,还有更快速更省空间的解法,也就是找出prices数组所有递增子区间,并将所有上升子空间中的最大值和最小值相减的值累加起来,这个和就是该问题的解:

class Solution {public int maxProfit(int[] prices) {int len = prices.length;if(len == 0) {return 0;}int st = 0;  //子区间开始段boolean flag = false;int ans = 0;for (int i = 1; i < len; i++) {if(prices[i] > prices[i - 1]) {if(!flag) {flag = true;st = i - 1; //标记开始段}continue;}if(flag) {ans += prices[i - 1] - prices[st];flag = false;}}if(flag) {ans += prices[len - 1] - prices[st];}return ans;}
}

该算法时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)

LeetCode 123:买卖股票的最佳时机III

给定一个数组,它的第 i i i 个元素是一支给定的股票在第 i i i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

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

示例 2:

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

示例 3:

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

分析

k = 2 k=2 k=2 的情况,按照最开始的解题模板即可:

import static java.lang.Math.*;
class Solution {public int maxProfit(int[] prices) {int len = prices.length;if(len == 0) {return 0;}int[][][] dp = new int[len][3][2];dp[0][0][0] = 0;dp[0][0][1] = Integer.MIN_VALUE >> 1;dp[0][1][1] = -prices[0];dp[0][1][0] = Integer.MIN_VALUE >> 1;dp[0][2][0] = Integer.MIN_VALUE >> 1;dp[0][2][1] = Integer.MIN_VALUE >> 1;for (int i = 1; i < len; i++) {for (int k = 2; k >= 1; k--) {dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]);}}return max(dp[len - 1][0][0], max(dp[len - 1][2][0], dp[len - 1][1][0]));}
}

LeetCode 188:买卖股票的最佳时机IV

给定一个数组,它的第 i i i 个元素是一支给定的股票在第 i i i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k k k 笔交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

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

示例 2:

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

分析

跟最上面解题模板一模一样,只不过要考虑 k k k 大于天数的情况。 k k k 大于天数时,把 k k k当成正无穷处理,防止内存超限,也就是和买卖股票的最佳时机II的解法一模一样。当 k k k 小于天数时,按照解题模板处理

class Solution {public int maxProfit(int k, int[] prices) {int len = prices.length;if(len == 0 || k == 0) {return 0;} else if(k > len) {return solve2(prices);  //当k大于天数时,当成k为正无穷判断,防止内存超限。}int[][][] dp = new int[len][k + 1][2];for (int t = 0; t <= k; t++) {dp[0][t][0] = Integer.MIN_VALUE >> 1;dp[0][t][1] = Integer.MIN_VALUE >> 1;}dp[0][0][0] = 0;dp[0][1][1] = -prices[0];for (int i = 1; i < len; i++) {for (int t = k; t >= 1; t--) {dp[i][t][0] = Math.max(dp[i - 1][t][0], dp[i - 1][t][1] + prices[i]);dp[i][t][1] = Math.max(dp[i - 1][t][1], dp[i - 1][t - 1][0] - prices[i]);}}int ans = 0;for (int i = 0; i <= k; i++) {ans = Math.max(ans, dp[len - 1][i][0]);}return ans;}private int solve2(int[] prices) {int len = prices.length;int[][] dp = new int[len][2];dp[0][0] = 0;dp[0][1] = -prices[0];for (int i = 1; i < len; 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[len - 1][0];}
}

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

给定一个整数数组,其中第 i i i 个元素代表了第 i i i 天的股票价格 。​
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

示例:

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

分析

这道题同样是 k = + ∞ k=+\infty k=+ 的情况,只不过加入了一个冷冻期的概念:当股票被售出后,只有到第三天才能购买。这意味当天的状态从原先的两种:持有股票、未持有股票变成了三种:持有股票、未持有股票、冷冻期。这几种状态的转移情况为:
在这里插入图片描述
所以我们将数组 d p dp dp 第三维度长度从2扩大到3,来表示当天的第三种状态。根据上述状态机示例图,我们可以推导出以下状态转移方程:

d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 2 ] ) dp[i][0]=max(dp[i-1][0],dp[i-1][2]) dp[i][0]=max(dp[i1][0],dp[i1][2]),前者对应由“未持股”->“什么也不做”,后者对应"冷冻期"->“什么也不做”
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] ) dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]) dp[i][1]=max(dp[i1][1],dp[i1][0]prices[i]),前者对应由“持股”->“什么也不做”,后者对应"未持股"->“买入”
d p [ i ] [ 2 ] = d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] dp[i][2]=dp[i-1][1]+prices[i] dp[i][2]=dp[i1][1]+prices[i],只有"持股"->"卖出"才能进入冷冻期这个状态。

最后的答案是 m a x ( d p [ n ] [ 0 ] , d p [ n ] [ 2 ] ) max(dp[n][0],dp[n][2]) max(dp[n][0],dp[n][2])

代码如下:

class Solution {public int maxProfit(int[] prices) {int len = prices.length;if(len == 0) {return 0;}int[][] dp = new int[len][3];dp[0][0] = 0;dp[0][1] = -prices[0];dp[0][2] = Integer.MIN_VALUE >> 1;  //不可能一开始就是冷冻期for (int i = 1; i < len; i++) {dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2]);dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);dp[i][2] = dp[i - 1][1] + prices[i];}return Math.max(dp[len - 1][0], dp[len - 1][2]);}
}

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

给定一个整数数组 p r i c e s prices prices,其中第 i i i 个元素代表了第 i i i 天的股票价格 ;非负整数 f e e fee fee 代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每次交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

示例 1:

输入: 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.

注意:

0 < p r i c e s . l e n g t h < = 50000. 0 < prices.length <= 50000. 0<prices.length<=50000.
0 < p r i c e s [ i ] < 50000 0 < prices[i] < 50000 0<prices[i]<50000
0 < = f e e < 50000 0 <= fee < 50000 0<=fee<50000

分析

k = + ∞ k=+\infty k=+ 的情况,只不过每次购买时都需要加上手续费,状态转移方程:

d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] − f e e ) dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]-fee) dp[i][0]=max(dp[i1][0],dp[i1][1]+prices[i]fee)
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i ] [ 0 ] − p r i c e s [ i ] ) dp[i][1]=max(dp[i-1][1],dp[i][0]-prices[i]) dp[i][1]=max(dp[i1][1],dp[i][0]prices[i])

代码:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/40981.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为了会做题而学习股票定价

目录 红利收益率。 市盈率。 折现现金流方法&#xff08;DCF&#xff09;。 折现红利模型&#xff08;DDM&#xff09;。 不变增长率&#xff0c;折现红利模型。 盈利和投资机会。 应用举例。 红利收益率。 市盈率。 折现现金流方法&#xff08;DCF&#xff09;。 认为…

【牛客刷题-算法】NC7 买卖股票的最好时机(一)

个人主页&#xff1a;清风莫追 系列专栏&#xff1a;牛客刷题——数据结构与算法 文章目录 1.题目描述2.算法设计思路3.代码实现4.运行结果 1.题目描述 描述 假设你有一个数组prices&#xff0c;长度为n&#xff0c;其中prices[i]是股票在第i天的价格&#xff0c;请根据这个价…

leetcode每日一题·买卖股票问题(Python)

leetcode每日一题买卖股票问题(Python) 买卖股票的最佳时机(股票最大利润) 题目链接 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。…

【题解】股票买卖

题目描述 最近越来越多的人都投身股市&#xff0c;阿福也有点心动了。谨记着“股市有风险&#xff0c;入市需谨慎”&#xff0c;阿福决定先来研究一下简化版的股票买卖问题。 假设阿福已经准确预测出了某只股票在未来N天的价格&#xff0c;他希望买卖两次&#xff0c;使得获得的…

一道关于股票买卖的算法编程题

前段时间在segmentfault回答了一个关于算法的问题,感觉很有趣,记录下来. 题目是这样的: 给定数组n&#xff0c;包含n天股票的价格price. 一个人一共最多可以买2手股票&#xff0c;但在第一手股票卖出前不能买入第二手股票。如果不买&#xff0c;收益为0.假设每手只买1股。计算…

股票买卖题型 详解

股票买卖题型 买卖股票最佳时机 ​ 第一题贪心算法应该快很多 就 不讲 。 此类问题 思路大致一致 ​ 第二题也可用贪心做 ans max(ans, ansprices[i]-prices[i-1]); 分析&#xff1a; 共有两个属性值 &#xff0c; 未持有 和持有股票 定义 f[ i ] [ 2 ] f[ i ] [ 0 ]表示 第…

【算法题】股票买卖问题解法详解

本解法是股票问题的通用解法&#xff0c;在leetcode上对应以下题&#xff1a; 买卖股票的最佳时机 买卖股票的最佳时机 II 买卖股票的最佳时机 III 买卖股票的最佳时机 IV 买卖股票的最佳时机含手续费 最佳买卖股票时机含冷冻期 下面来说通用解法&#xff1a; 这类问题…

贪心算法(股票买卖例题)

贪心算法定义&#xff1a; 贪心算法&#xff08;又称贪婪算法&#xff09;是指&#xff0c;在对 问题求解时&#xff0c;总是做出在当前看来是最好的选择。也就是说&#xff0c;不从整体最优上加以考虑&#xff0c;他所做出的是在某种意义上的局部 最优解。 贪心算法不是对所有…

买卖股票的最佳时机 II -数学推导证明贪心思路 -leetcode122

问题说明来源leetcode 一、问题描述: 122. 买卖股票的最佳时机 II 难度中等1941 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可…

产品开发利器:Axure及实例

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 文章目录 简介Axure优点Axure和蓝湖Axure实例 简介 Axure是一个最便捷、最热门的界面原型设计工具&#xff0c;它不需要任何编程或写代码基础&#xff0c;就可以设…

ChatGPT伪原创:智能AI助手助力文章创作

智能AI助手助力文章创作 随着人工智能技术的不断发展&#xff0c;智能AI助手正逐渐成为文章创作的得力工具。无论是在写作过程中提供灵感和创意&#xff0c;还是在文章编辑和校对中提供帮助&#xff0c;智能AI助手都能为作者节省时间和精力&#xff0c;提高文章质量。本文将从…

2022年度强化学习领域19个重要进展汇总

本文汇总梳理了2022年度&#xff0c;强化学习领域的发展重大事件、以及落地应用等方向中突出代表&#xff0c;整理难免带有个人观点&#xff0c;欢迎大家一起讨论。本文整理自“深度强化学习实验室”公众号&#xff0c;阅读原文请点击这里。 【1】MIT强化学习新算法EIPO&#…

分割一切?手把手教你部署SAM+LabelStudio实现自动标注

一&#xff0c;前言 最近Open-mmlab开源了Playground项目&#xff0c;将最近引起CV界轰动的SAM(Segment Anything Model)模型和Open-mmlab多个视觉框架相结合&#xff0c;可实现多种视觉任务的自动标注&#xff0c;本文将采用Open-mmlab的Playground开源项目&#xff0c;使用S…

老婆饼真有老婆,驴肉火烧有头驴--文言一心

困扰大家很久的问题&#xff1a;老婆饼里为啥没老婆&#xff0c;鱼香肉丝里为啥没有鱼。 最近&#xff0c;百度推出自己的AI大模型“文心一言”&#xff0c;李彦宏在发布会上表示&#xff0c;目前百度是全球大厂中第一个做出对标ChatGPT产品的企业。 有网友让文心一言作画&…

【2023,学点儿新Java-15】案例分享:基于Java实现餐厅点餐系统(附完整源代码)

前情回顾&#xff1a; 【2023&#xff0c;学点儿新Java-14】携程面试题&#xff1a;如何看待Java是一门半编译半解释型的语言&#xff1f;| 咨询互联网行业 资深前辈的一些问题 | 附&#xff1a;为什么说ChatGPT的核心算法是…&#xff1f;| GPT-3.5【2023&#xff0c;学点儿新…

文心一言作画:有点东西但不多...

随着ChatGPT的持续火热 与AI领域有关的话题 是越来越热闹了 前几天百度发布 “文心一言” 自然也成了网友们 重点关注的对象 不过大家的目光主要还是集中在 文心一言的绘画功能上 在人工智能加成下出来的画面 一个比一个绝 成功颠覆 大家对绘画的认知 生意火爆的商铺…

网传文心一言的魔性作图,有点被吓到...

来源&#xff1a;菜鸟教程 近日看到网友们用百度文心一言来作图&#xff0c;看了后我都愣住了。。。 1、AI 作画 -- 车水马龙 2、AI 作画 -- 驴肉火烧 3、AI 作画 -- 唐伯虎点秋香 4、AI 作画 -- 鱼香肉丝 5、AI 作画 -- 胸有成竹 6、AI 作画 -- 夫妻肺片 7、AI 作画 -- 红烧狮…

文心一言的魔性作图,我愣住了。。。

点关注公众号&#xff0c;回复“1024”获取2TB学习资源&#xff01; 最近&#xff0c;百度推出自己的AI大模型“文心一言”&#xff0c;李彦宏在发布会上表示&#xff0c;目前百度是全球大厂中第一个做出对标 ChatGPT 产品的企业。 但是&#xff0c;今天看到网友们用它来作图&a…

那些在学习GPT的过程中学到的

1、大模型是什么 GPT横空出世之后&#xff0c;大模型火了&#xff0c;什么是大模型呐&#xff1f; 大模型通常指的是具有大规模参数和复杂结构的深度学习模型。它们的设计和结构可以因任务而异&#xff0c;但以下是一些常见的大模型结构&#xff1a; Transformer&#xff1a…

LangChain 介绍及相关组件使用总结

一、langChain LangChain 是一个由语言模型LLMs驱动的应用程序框架&#xff0c;它允许用户围绕大型语言模型快速构建应用程序和管道。 可以直接与 OpenAI 的 ChatGPT 模型以及 Hugging Face 集成。通过 langChain 可快速构建聊天机器人、生成式问答(GQA)、本文摘要等应用场景。…