|
| 1 | +## 题目地址(714. 买卖股票的最佳时机含手续费) |
| 2 | + |
| 3 | +https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/ |
| 4 | + |
| 5 | +## 题目描述 |
| 6 | + |
| 7 | +``` |
| 8 | +给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。 |
| 9 | +
|
| 10 | +你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。 |
| 11 | +
|
| 12 | +返回获得利润的最大值。 |
| 13 | +
|
| 14 | +注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。 |
| 15 | +
|
| 16 | +示例 1: |
| 17 | +
|
| 18 | +输入: prices = [1, 3, 2, 8, 4, 9], fee = 2 |
| 19 | +输出: 8 |
| 20 | +解释: 能够达到的最大利润: |
| 21 | +在此处买入 prices[0] = 1 |
| 22 | +在此处卖出 prices[3] = 8 |
| 23 | +在此处买入 prices[4] = 4 |
| 24 | +在此处卖出 prices[5] = 9 |
| 25 | +总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8. |
| 26 | +
|
| 27 | +注意: |
| 28 | +
|
| 29 | +0 < prices.length <= 50000. |
| 30 | +0 < prices[i] < 50000. |
| 31 | +0 <= fee < 50000. |
| 32 | +``` |
| 33 | + |
| 34 | +## 前置知识 |
| 35 | + |
| 36 | +- 动态规划 |
| 37 | + |
| 38 | +## 公司 |
| 39 | + |
| 40 | +- 暂无 |
| 41 | + |
| 42 | +## 记忆化递归 |
| 43 | + |
| 44 | +### 思路 |
| 45 | + |
| 46 | +首先明确一点。 如果没有交易费用,那么和另一道力扣的简单难度题目一样。 多了交易费用导致问题稍微复杂了一点。加了交易费用有什么不同呢? |
| 47 | + |
| 48 | +举一个例子大家就明白了。 比如 prices = [1,1,2,2,6] fee = 3。 如果没有按照无交易费的原则**只要有利可图就交易**,那么我们的收益为 1。实际上,在 1 买入, 6 卖出却可以得到 3 的收益。其根本原因在于**交易次数对结果是有影响的**。 |
| 49 | + |
| 50 | +这道题不能使用上述的贪心策略,而必须使用动态规划。 |
| 51 | + |
| 52 | +> 动态规划和贪心有着很强的关联性。 |
| 53 | +
|
| 54 | +定义 dp[i] 为到第 i 天(0=< i <n)为止的收益,那么答案就是 dp[n-1]。但是由于题目限定了只能**在手上没有股票的时候才能买,手上有股票的时候才能卖(这很显然)**。因此我们需要多定义一个维度的状态,表示现在手上是否有股票。标识是否手上有股票只需要一个长度为 2 的列表就可以了。 |
| 55 | + |
| 56 | +因此我们可以使用 dp[i][0] 表示没有股票的情况下的最大利润,dp[i][1] 表示有股票的情况下的最大利润。 |
| 57 | + |
| 58 | +那么: |
| 59 | + |
| 60 | +- 如果第 i 天手上没有股票,要么是 i - 1 没股票,今天什么都不做。要么是 i - 1 手上有股票,今天卖了它。因此 dp[i][0] 就是这两种情况的利润最大值。 |
| 61 | + |
| 62 | +- 如果第 i 天手上有股票,要么是 i - 1 有股票,今天什么都不做。要么是 i - 1 手上没有股票,今天买了它。因此 dp[i][0] 就是这两种情况的利润最大值。 |
| 63 | + |
| 64 | +最后返回 dp[n-1][0] 即可。 |
| 65 | + |
| 66 | +> 由于 dp[n-1][1] <= dp[n-1][0] ,因此直接返回 dp[n-1][0] 即可。 |
| 67 | +
|
| 68 | +base case 见下方代码的递归出口。 |
| 69 | + |
| 70 | +### 关键点 |
| 71 | + |
| 72 | +- 记忆化递归 |
| 73 | + |
| 74 | +### 代码 |
| 75 | + |
| 76 | +- 语言支持:Python3 |
| 77 | + |
| 78 | +Python3 Code: |
| 79 | + |
| 80 | +```python |
| 81 | + |
| 82 | +class Solution: |
| 83 | + def maxProfit(self, prices: List[int], fee: int) -> int: |
| 84 | + def dp(i): |
| 85 | + if i == 0: |
| 86 | + return 0, -prices[0] - fee |
| 87 | + sell, buy = dp(i - 1) |
| 88 | + return max(sell, buy + prices[i]), max(buy, sell - prices[i] - fee) |
| 89 | + |
| 90 | + return dp(len(prices) - 1)[0] |
| 91 | + |
| 92 | +``` |
| 93 | + |
| 94 | +**复杂度分析** |
| 95 | + |
| 96 | +令 n 为数组长度。 |
| 97 | + |
| 98 | +- 时间复杂度:$O(n)$ |
| 99 | +- 空间复杂度:$O(n)$ |
| 100 | + |
| 101 | +## DP |
| 102 | + |
| 103 | +### 思路 |
| 104 | + |
| 105 | +使用 dp 的思路和上面一样,只是代码形式不同而已。 |
| 106 | + |
| 107 | +### 关键点 |
| 108 | + |
| 109 | +- 滚动数组优化技巧 |
| 110 | + |
| 111 | +### 代码 |
| 112 | + |
| 113 | +- 语言支持:Python3 |
| 114 | + |
| 115 | +Python3 Code: |
| 116 | + |
| 117 | +```py |
| 118 | + |
| 119 | +class Solution: |
| 120 | + def maxProfit(self, prices: List[int], fee: int) -> int: |
| 121 | + n = len(prices) |
| 122 | + dp = [[0 for i in range(2)]] * n |
| 123 | + for i in range(n): |
| 124 | + if i == 0: |
| 125 | + dp[i][0] = 0 |
| 126 | + dp[i][1] = -1 * prices[i] |
| 127 | + else: |
| 128 | + dp[i][0] = max(dp[i - 1][1] + prices[i] - fee, dp[i - 1][0]) |
| 129 | + dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]) |
| 130 | + |
| 131 | + return dp[-1][0] |
| 132 | + |
| 133 | +``` |
| 134 | + |
| 135 | +**复杂度分析** |
| 136 | + |
| 137 | +令 n 为数组长度。 |
| 138 | + |
| 139 | +- 时间复杂度:$O(n)$ |
| 140 | +- 空间复杂度:$O(n)$ |
| 141 | + |
| 142 | +使用 DP 数组的好处就是可以使用滚动数组优化。比如这道题使用滚动数组可将空间进一步压缩。 |
| 143 | + |
| 144 | +```py |
| 145 | +class Solution: |
| 146 | + def maxProfit(self, prices: List[int], fee: int) -> int: |
| 147 | + n = len(prices) |
| 148 | + # [手里没股票, 手里有股票] |
| 149 | + dp = [0, 0] |
| 150 | + for i in range(n): |
| 151 | + if i == 0: |
| 152 | + dp[0] = 0 |
| 153 | + dp[1] = -1 * prices[i] - fee |
| 154 | + else: |
| 155 | + dp[0] = max(dp[0], dp[1] + prices[i]) |
| 156 | + dp[1] = max(dp[1], dp[0] - prices[i] - fee) |
| 157 | + |
| 158 | + return dp[0] |
| 159 | +``` |
| 160 | + |
| 161 | +**复杂度分析** |
| 162 | + |
| 163 | +令 n 为数组长度。 |
| 164 | + |
| 165 | +- 时间复杂度:$O(n)$ |
| 166 | +- 空间复杂度:$O(1)$ |
| 167 | + |
| 168 | +> 此题解由 [力扣刷题插件](https://leetcode-pp.github.io/leetcode-cheat/?tab=solution-template) 自动生成。 |
| 169 | +
|
| 170 | +力扣的小伙伴可以[关注我](https://leetcode-cn.com/u/fe-lucifer/),这样就会第一时间收到我的动态啦~ |
| 171 | + |
| 172 | +以上就是本文的全部内容了。大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 40K star 啦。大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。 |
| 173 | + |
| 174 | +关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。 |
| 175 | + |
| 176 | + |
0 commit comments