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=+ 的情况,只不过加入了一个冷冻期的概念:当股票被售出后,只有到第三天才能购买。这意味当天的状态从原先的两种:持有股票、未持有股票变成了三种:持有股票、未持有股票、冷冻期。这几种状态的转移情况为:
LeetCode股票买卖问题通用解法_第1张图片
所以我们将数组 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];
    }
}

你可能感兴趣的:(LeetCode股票买卖问题通用解法)