# 188. Best Time to Buy and Sell Stock IV

**<font color=red>难度: Hard</font>**

## 刷题内容

> 原题连接

* https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/description/

> 内容描述

```
Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).

Example 1:

Input: [3,3,5,0,0,3,1,4]
Output: 6
Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
             Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3.
Example 2:

Input: [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
             Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are
             engaging multiple transactions at the same time. You must sell before buying again.
Example 3:

Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
```

## 解题方案

参考[meng789987](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/discuss/135704/detail-explanation-of-dp-solution/150296)

> 思路 1
******- 时间复杂度: O(k*N^2)******- 空间复杂度: O(k*N)******

dp[k][i]代表在第i天完成第k次交易的最大profit

因此状态方程为dp[k][i] = max(dp[k][i-1], prices[i]-prices[j]+dp[k-1][j-1])，其中0 < j <= i

意思就是在第i天完成第k次交易的最大profit就是，在```第i-1天完成第k次交易的最大profit```与
```在j-1天完成前k-1次交易的最大profit（然后在第j天最后一次买入股票，并在第i天卖出）```两者中的最大值

另外这里还有一个trick就是当K >= len(prices)//2 的时候，实际上就跟无限次交易一样了

但是这样还是超时了

```
class Solution(object):
    def maxProfit(self, k, prices):
        """
        :type k: int
        :type prices: List[int]
        :rtype: int
        """
        K = k
        if K >= len(prices) // 2: 
            return sum([max(prices[i+1]-prices[i], 0) for i in range(len(prices)-1)])
        if not prices or len(prices) == 0:
            return 0
        dp = [[0] * len(prices) for i in range(K+1)]
        for k in range(1, K+1):
            for i in range(1, len(prices)):
                min_ = prices[0]
                for j in range(1, i+1):
                    min_ = min(min_, prices[j]-dp[k-1][j-1])
                dp[k][i] = max(dp[k][i-1], prices[i]-min_)
        return dp[-1][-1]
```


> 思路 2
******- 时间复杂度: O(k*N)******- 空间复杂度: O(k*N)******


我们可以通过多加一个min_列表来记录prices[i]-prices[j]+dp[k-1][j-1]，这样不用每次都重新计算1 <= j <= i的循环

beats 61.47%

```python
class Solution(object):
    def maxProfit(self, k, prices):
        """
        :type k: int
        :type prices: List[int]
        :rtype: int
        """
        K = k
        if K >= len(prices) // 2: 
            return sum([max(prices[i+1]-prices[i], 0) for i in range(len(prices)-1)])
        if not prices or len(prices) == 0:
            return 0
        dp, min_ = [[0] * len(prices) for i in range(K+1)], [prices[0]] * (K+1)
        for i in range(1, len(prices)):
            for k in range(1, K+1):
                min_[k] = min(min_[k], prices[i]-dp[k-1][i-1])
                dp[k][i] = max(dp[k][i-1], prices[i]-min_[k])
        return dp[-1][-1]
```



> 思路 3
******- 时间复杂度: O(k*N)******- 空间复杂度: O(k)******


想想看，其实空间还可以压缩，那就是跟刚才思路1到思路2的改进思想一样，我们把dp[k][i]也全部记录下来，其中0 <= i <= len(prices)，

意思就是说现在的dp[k]就等于原来的min([dp[k][i] for i in range(0, len(prices)])

beats 90.54%

```python
class Solution(object):
    def maxProfit(self, k, prices):
        """
        :type k: int
        :type prices: List[int]
        :rtype: int
        """
        K = k
        if K >= len(prices) // 2: 
            return sum([max(prices[i+1]-prices[i], 0) for i in range(len(prices)-1)])
        if not prices or len(prices) == 0:
            return 0
        dp, min_ = [0] * (K+1), [prices[0]] * (K+1)
        for i in range(1, len(prices)):
            for k in range(1, K+1):
                min_[k] = min(min_[k], prices[i]-dp[k-1])
                dp[k] = max(dp[k], prices[i]-min_[k])
        return dp[-1]
```























