LeetCode_109_BestTimeToBuyAndSellStockWithTransactionFee


1. question: 买卖股票的最佳时机含手续费(中等)

给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee

示例 1:

1
2
3
4
5
6
7
8
输入: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

示例 2:

1
2
输入:prices = [1,3,7,5,10,3], fee = 3
输出:6

提示:

1
2
3
1 <= prices.length <= 5 * 104
1 <= prices[i] < 5 * 104
0 <= fee < 5 * 104

2. answers

这道题和买卖股票还是有区别的,因为存在交易费的缘故,所以连续天的收益不再等于单天的收益和。不过,总收益可以认为是若干个子收益之和。

首先一个难点就是如何判断是交易并重新买入,而不是在前面买入的基础上持续持有。

1
2
3
4
5
6
7
8
9
从没有交易费的角度来看,[1,8]的利润是等于[1,3]+[3,8]的。如果[4,8]的利润大于[3,8]的话(即[3,4]是负利润),显然[1,8] < [1,3] + [4,8]。

但是存在交易费,要想满足上述条件,[3,4]之间的负利润应该大于交易费,即 [3] - [4] > 交易费。也就是 [4] + 交易费 < [3]。否则因为交易费的缘故,就不如持续持有的利润高。

当满足上述条件时,即进行一次交易。这样才能保证存在交易费的情况下,两段交易优于一段交易

如果不满足条件,即 [4] + 交易费 > [3],这样的话,如果后续遇到比[3]大的值,比如在[10],显然是 [1,10] > [1,3] - 交易费 + [4,10]

如果遇不到比[3]大的,说明后续无论怎么交易,都只能是负利润。

所以说,(在已经买入的情况下)如果后续的值小于当前值,并且差值大于交易费,即就应该重新开始一次交易。

那么什么时候买入呢?(在没有买入的情况下)显然如果后面的值小于当前的值,肯定是越低越买入。

那么如何在考虑手续费的同时计算收益呢?在买入的时候要先将手续费加入,只要利润高于花销,那么就卖出(注意,后续还要判断是否真正卖出)。如果没有真正卖出,即等同于买卖股票,连续天的收益等于单天的收益和(因为最开始已经记录了手续费了)。

代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Solution_0109 {

public int maxProfit(int[] prices, int fee) {

// 初始化,第一天买入
int buy = prices[0] + fee;

int sum = 0;

for (int p : prices) {

// 判断是否是最低值。是,则计算利润;不是,则更新其为这天买入(即此时是最低值,要么是初次买入,要么是重新买入)
if (p + fee < buy) {
buy = p + fee;
} else if (p > buy){

// 计算加入这天卖出的利润
sum += p - buy;

// 同时这天买入。(这是假设买入,判断后续是否是当前片段的最低值,如果是,那么就存在利润)
// 这里为什么不加上手续费呢?(这里是因为,如果这天不卖出,后面的利润可能更大。仅仅是假装卖出)
buy = p;
}
}
return sum;
}

public static void main(String[] args) {
System.out.println();

int[] prices = {1, 3, 2, 8, 4, 9};
int fee = 2;

Solution_0109 s = new Solution_0109();

System.out.println(s.maxProfit(prices, fee));
}
}

这道题采用贪心的想法,并不容易理解。应该采用动态规划。在做了买卖股票系列题目之后,本题只需要在买入的时候额外减去手续费即可。代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Solution_0109_04 {

public int maxProfit(int[] prices, int fee) {

// 定义数组,并初始化,注意,买入的时候减去手续费
int[][] dp = new int[prices.length][2];
dp[0][0] = -prices[0] - fee;
dp[0][1] = 0;

for (int i = 1; i < prices.length; 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-1][0] + prices[i]);
}

for (int i = 0; i < prices.length; i++) {
for (int j = 0; j < 2; j++) {
System.out.print(dp[i][j] + "\t");
}
System.out.println();
}

return dp[prices.length - 1][1];
}

public static void main(String[] args) {
System.out.println();

Solution_0109_04 s = new Solution_0109_04();

// int[] prices = {1, 3, 2, 8, 4, 9};
// int fee = 2;

int[] prices = {1,3,7,5,10,3};
int fee = 3;

System.out.println(s.maxProfit(prices, fee));
}
}

3. 备注

参考力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 (leetcode-cn.com)代码随想录 (programmercarl.com)


文章作者: 浮云
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 浮云 !
  目录