动态规划—714. 买卖股票的最佳时机含手续费
- 题目描述
- 前言
- 基本思路
- 1. 问题定义
- 2. 理解问题和递推关系
- 状态定义:
- 状态转移公式:
- 初始条件:
- 3. 解决方法
- 动态规划方法
- 伪代码:
- 4. 进一步优化
- 5. 小总结
- Python代码
- Python代码解释总结:
- C++代码
- C++代码解释总结:
- 总结
题目描述
前言
带手续费的买卖股票问题 是动态规划的经典问题之一。给定一个数组 prices
表示股票每天的价格,并且每次卖出股票时需要支付一定的手续费 fee
,目标是通过在任意天买入或卖出股票,以最大化利润。该问题要求我们考虑每次交易时手续费的影响,并通过动态规划的方法来规划最优的买卖操作。
基本思路
1. 问题定义
给定一个数组 prices
,其中 prices[i]
表示第 i
天的股票价格,同时给定一个整数 fee
代表交易时需要支付的手续费。目标是通过多次交易获得最大利润(可以多次买卖股票),但每次交易(买入和卖出)需要支付 fee
。
2. 理解问题和递推关系
为了帮助我们理解这个问题,我们可以定义两种状态来表示每一天的持有情况。
状态定义:
-
持有股票的状态(
hold
):hold[i]
表示在第i
天结束时,我们持有股票时的最大收益。- 这个状态可以从两种情况转移而来:要么是之前已经持有并继续持有(
hold[i-1]
),要么是今天刚刚买入股票。
-
未持有股票的状态(
sell
):sell[i]
表示在第i
天结束时,我们不持有股票时的最大收益。- 这个状态可以从两种情况转移而来:要么是之前已经不持有并继续不持有(
sell[i-1]
),要么是今天卖出了股票。
状态转移公式:
-
持有股票的状态转移:要么我们继续持有股票,要么今天刚刚买入股票:
h o l d [ i ] = max ( h o l d [ i − 1 ] , s e l l [ i − 1 ] − p r i c e s [ i ] ) hold[i] = \max(hold[i-1], sell[i-1] - prices[i]) hold[i]=max(hold[i−1],sell[i−1]−prices[i])hold[i-1]
表示我们在i-1
天已经持有股票且继续持有;sell[i-1] - prices[i]
表示今天买入股票的收益。
-
未持有股票的状态转移:要么我们继续不持有股票,要么今天卖出了股票并获得利润:
s e l l [ i ] = max ( s e l l [ i − 1 ] , h o l d [ i − 1 ] + p r i c e s [ i ] − f e e ) sell[i] = \max(sell[i-1], hold[i-1] + prices[i] - fee) sell[i]=max(sell[i−1],hold[i−1]+prices[i]−fee)sell[i-1]
表示我们在i-1
天不持有股票且继续不持有;hold[i-1] + prices[i] - fee
表示今天卖出股票的收益,需要减去交易手续费fee
。
初始条件:
hold[0] = -prices[0]
:表示在第 0 天买入股票后的收益(即负的股票价格)。sell[0] = 0
:在第 0 天不持有股票,收益为 0。
3. 解决方法
动态规划方法
- 初始化状态:
hold[0]
和sell[0]
表示初始的持有和不持有股票的状态。 - 状态转移:通过递推公式更新每一天的持有股票和不持有股票状态。
- 最终结果:在最后一天,最大收益必然是不持有股票时的收益,即
sell[n-1]
。
伪代码:
initialize hold[0] = -prices[0], sell[0] = 0
for each day i from 1 to n-1:
hold[i] = max(hold[i-1], sell[i-1] - prices[i])
sell[i] = max(sell[i-1], hold[i-1] + prices[i] - fee)
return sell[n-1]
4. 进一步优化
- 空间优化:由于
dp[i]
只依赖于dp[i-1]
,我们可以将hold
和sell
的状态用常量来存储,优化空间复杂度为O(1)
。
5. 小总结
- 问题思路:通过将状态分为持有股票和不持有股票的两种情况,动态规划可以清晰描述每一天的操作。
- 时间复杂度:该方法的时间复杂度为
O(n)
,空间复杂度可以优化为O(1)
。
以上就是买卖股票的最佳时机含手续费问题的基本思路。
Python代码
class Solution:
def maxProfit(self, prices: list[int], fee: int) -> int:
if not prices:
return 0
# 初始化第0天的状态
hold = -prices[0]
sell = 0
for i in range(1, len(prices)):
# 更新状态
new_hold = max(hold, sell - prices[i])
new_sell = max(sell, hold + prices[i] - fee)
# 更新持有和不持有股票的状态
hold, sell = new_hold, new_sell
# 返回不持有股票时的最大收益
return sell
Python代码解释总结:
- 初始化:在第 0 天,如果持有股票,则收益为
-prices[0]
,否则为0
。 - 状态转移:通过递推公式更新每一天的
hold
和sell
状态。 - 返回结果:最终返回不持有股票时的最大收益
sell
。
C++代码
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
if (prices.empty()) return 0;
int hold = -prices[0]; // 初始化第0天持有股票的状态
int sell = 0; // 初始化第0天不持有股票的状态
for (int i = 1; i < prices.size(); ++i) {
// 更新持有和不持有股票的状态
int new_hold = max(hold, sell - prices[i]);
int new_sell = max(sell, hold + prices[i] - fee);
hold = new_hold;
sell = new_sell;
}
// 返回不持有股票时的最大收益
return sell;
}
};
C++代码解释总结:
- 初始化:在第 0 天,如果持有股票,则收益为
-prices[0]
,否则为0
。 - 状态转移:通过递推公式更新每一天的
hold
和sell
状态。 - 返回结果:在最后一天,返回不持有股票时的最大收益
sell
。
总结
- 问题核心:带手续费的买卖股票问题通过将状态划分为持有股票和不持有股票两种情况,通过动态规划可以有效求解。
- 时间复杂度:时间复杂度为
O(n)
,适合处理大规模输入。 - 空间优化:由于每个状态只依赖前一天的状态,因此可以将空间复杂度优化为
O(1)
,这在处理大规模输入时是非常有效的。