* [做项目(多个C++、Java、Go、测开、前端项目)](https://www.programmercarl.com/other/kstar.html) * [刷算法(两个月高强度学算法)](https://www.programmercarl.com/xunlian/xunlianying.html) * [背八股(40天挑战高频面试题)](https://www.programmercarl.com/xunlian/bagu.html) # 714.买卖股票的最佳时机含手续费 [力扣题目链接](https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/) 给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 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 < prices.length <= 50000. * 0 < prices[i] < 50000. * 0 <= fee < 50000. ## 算法公开课 **[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html):[动态规划来决定最佳时机,这次含手续费!| LeetCode:714.买卖股票的最佳时机含手续费](https://www.bilibili.com/video/BV1z44y1Z7UR),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 ## 思路 本题贪心解法:[贪心算法:买卖股票的最佳时机含手续费](https://programmercarl.com/0714.买卖股票的最佳时机含手续费.html) 性能是: * 时间复杂度:O(n) * 空间复杂度:O(1) 本题使用贪心算法并不好理解,也很容易出错,那么我们再来看看使用动规的方法如何解题。 相对于[动态规划:122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II(动态规划).html),本题只需要在计算卖出操作的时候减去手续费就可以了,代码几乎是一样的。 唯一差别在于递推公式部分,所以本篇也就不按照动规五部曲详细讲解了,主要讲解一下递推公式部分。 这里重申一下dp数组的含义: dp[i][0] 表示第i天持有股票所得最多现金。 dp[i][1] 表示第i天不持有股票所得最多现金 如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来 * 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0] * 第i天买入股票,所得现金就是昨天不持有股票的所得现金减去 今天的股票价格 即:dp[i - 1][1] - prices[i] 所以:dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); 在来看看如果第i天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来 * 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1] * 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金,**注意这里需要有手续费了**即:dp[i - 1][0] + prices[i] - fee 所以:dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee); **本题和[动态规划:122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II(动态规划).html)的区别就是这里需要多一个减去手续费的操作**。 以上分析完毕,C++代码如下: ```CPP class Solution { public: int maxProfit(vector& prices, int fee) { int n = prices.size(); vector> dp(n, vector(2, 0)); dp[0][0] -= prices[0]; // 持股票 for (int i = 1; i < n; i++) { dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee); } return max(dp[n - 1][0], dp[n - 1][1]); } }; ``` * 时间复杂度:O(n) * 空间复杂度:O(n) ## 其他语言版本 ### Java: ```java /** * 卖出时支付手续费 * @param prices * @param fee * @return */ public int maxProfit(int[] prices, int fee) { int len = prices.length; // 0 : 持股(买入) // 1 : 不持股(售出) // dp 定义第i天持股/不持股 所得最多现金 int[][] dp = new int[len][2]; dp[0][0] = -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][0] + prices[i] - fee, dp[i - 1][1]); } return Math.max(dp[len - 1][0], dp[len - 1][1]); } /** * 买入时支付手续费 * @param prices * @param fee * @return */ public int maxProfit(int[] prices, int fee) { int len = prices.length; // 0 : 持股(买入) // 1 : 不持股(售出) // dp 定义第i天持股/不持股 所得最多现金 int[][] dp = new int[len][2]; // 考虑买入的时候就支付手续费 dp[0][0] = -prices[0] - fee; 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][0] + prices[i], dp[i - 1][1]); } return Math.max(dp[len - 1][0], dp[len - 1][1]); } // 一维数组优化 class Solution { public int maxProfit(int[] prices, int fee) { int[] dp = new int[2]; dp[0] = -prices[0]; dp[1] = 0; for (int i = 1; i <= prices.length; i++) { dp[0] = Math.max(dp[0], dp[1] - prices[i - 1]); dp[1] = Math.max(dp[1], dp[0] + prices[i - 1] - fee); } return dp[1]; } } ```Java //使用 2*2 array class Solution { public int maxProfit(int[] prices, int fee) { int dp[][] = new int[2][2]; int len = prices.length; //[i][0] = holding the stock //[i][1] = not holding the stock dp[0][0] = -prices[0]; for(int i = 1; i < len; i++){ dp[i % 2][0] = Math.max(dp[(i - 1) % 2][0], dp[(i - 1) % 2][1] - prices[i]); dp[i % 2][1] = Math.max(dp[(i - 1) % 2][1], dp[(i - 1) % 2][0] + prices[i] - fee); } return dp[(len - 1) % 2][1]; } } ``` ### python ```python class Solution: def maxProfit(self, prices: List[int], fee: int) -> int: n = len(prices) dp = [[0] * 2 for _ in range(n)] dp[0][0] = -prices[0] #持股票 for i in range(1, n): dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i]) dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i] - fee) return max(dp[-1][0], dp[-1][1]) ``` ```python class Solution: def maxProfit(self, prices: List[int], fee: int) -> int: # 持有股票手上的最大現金 hold = -prices[0] - fee # 不持有股票手上的最大現金 not_hold = 0 for price in prices[1:]: new_hold = max(hold, not_hold - price - fee) new_not_hold = max(not_hold, hold + price) hold, not_hold = new_hold, new_not_hold return not_hold ``` ### Go: ```go // 买卖股票的最佳时机含手续费 动态规划 // 时间复杂度O(n) 空间复杂度O(n) func maxProfit(prices []int, fee int) int { n := len(prices) dp := make([][2]int, n) dp[0][0] = -prices[0] for i := 1; i < n; i++ { dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i] - fee) dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i]) } return dp[n-1][1] } func max(a, b int) int { if a > b { return a } return b } ``` ### JavaScript: ```javascript const maxProfit = (prices,fee) => { let dp = Array.from(Array(prices.length), () => Array(2).fill(0)); dp[0][0] = 0 - prices[0]; for (let 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][0] + prices[i] - fee, dp[i - 1][1]); } return Math.max(dp[prices.length - 1][0], dp[prices.length - 1][1]); } ``` ### TypeScript: ```typescript function maxProfit(prices: number[], fee: number): number { /** dp[i][0]:持有股票 dp[i][1]: 不持有 */ const length: number = prices.length; if (length === 0) return 0; const dp: number[][] = new Array(length).fill(0).map(_ => []); dp[0][0] = -prices[0]; dp[0][1] = 0; for (let i = 1; i < 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[length - 1][1]; }; ``` ### C: ```c #define max(a, b) ((a) > (b) ? (a) : (b)) // dp[i][0] 表示第i天持有股票所省最多现金。 // dp[i][1] 表示第i天不持有股票所得最多现金 int maxProfit(int* prices, int pricesSize, int fee) { int dp[pricesSize][2]; dp[0][0] = -prices[0]; dp[0][1] = 0; for (int i = 1; i < pricesSize; ++i) { dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee); } return dp[pricesSize - 1][1]; } ``` ### Rust: **贪心** ```Rust impl Solution { pub fn max_profit(prices: Vec, fee: i32) -> i32 { let mut result = 0; let mut min_price = prices[0]; for i in 1..prices.len() { if prices[i] < min_price { min_price = prices[i]; } // if prices[i] >= min_price && prices[i] <= min_price + fee { continue; } if prices[i] > min_price + fee { result += prices[i] - min_price - fee; min_price = prices[i] - fee; } } result } } ``` **动态规划** ```Rust impl Solution { pub fn max_profit(prices: Vec, fee: i32) -> i32 { let mut dp = vec![vec![0; 2]; prices.len()]; dp[0][0] = -prices[0]; for (i, &p) in prices.iter().enumerate().skip(1) { dp[i][0] = dp[i - 1][0].max(dp[i - 1][1] - p); dp[i][1] = dp[i - 1][1].max(dp[i - 1][0] + p - fee); } dp[prices.len() - 1][1] } } ``` **动态规划空间优化** ```rust impl Solution { pub fn max_profit(prices: Vec, fee: i32) -> i32 { let (mut low, mut res) = (-prices[0], 0); for p in prices { low = low.max(res - p); res = res.max(p + low - fee); } res } } ```