原题
Your are given an array of integers prices, for which the i-th element is the price of a given stock on day i; and a non-negative integer fee representing a transaction fee.
You may complete as many transactions as you like, but you need to pay the transaction fee for each transaction. You may not buy more than 1 share of a stock at a time (ie. you must sell the stock share before you buy again.)
Return the maximum profit you can make.
Example 1:
Input: prices = [1, 3, 2, 8, 4, 9], fee = 2
Output: 8
Explanation: The maximum profit can be achieved by:
Buying at prices[0] = 1
Selling at prices[3] = 8
Buying at prices[4] = 4
Selling at prices[5] = 9
The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8.
leetcode地址: https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/description/
解题思路
题目大意:有一支股票,每天的价格都有变动。你每天都可以选择买入或者卖出中的一种操作;而且股票的最大持有数为1。每次卖出时,会收取固定的手续费。现在给出股票的价格序列以及手续费,求能够获取的最大利润。
这是典型的动态规划题型,其主要的难点就是在于定义“状态”以及找出状态转移方程。
我在一开始的思路是这样的:设定状态F(n)为n天内的最大利润。而状态转移方程是什么呢?
要获取最大利润,那么最后一天结束后,手里肯定没有股票。所以最后的一个操作就是“卖出”。既然有卖出,肯定就有买入。假设最后一次买入的日期为x,卖出的日期为y, 那么在第x天之前的最大利润是F(x-1),刚好是一个子状态。设价格表为V, 所以这时候的总利润就是F(x-1) + V(y) - V(x) - fee。我们把x与y的所有组合都计算一遍,就可以得出最大利润了。
通过以上分析,我们就可以列出状态转移方程:
F(n) = max(V(y) - V(x) - fee + F(x-1)) (1 < y <= n, 1 <= x < y)
根据分析,我高高兴兴地写出代码,然后提交: Time Limit Exceeded。
分析一下以上算法的时间复杂度:对于x与y的组合,有(n^2)/ 2 种可能, 所以复杂度为O(n^2)。
很明显嘛,真正优秀的DP算法不应该有这么高的复杂度的。一定是有条件漏了没有考虑清楚。
然后我学习了讨论区的解析,发现只需要几行代码就可以解决问题,时间复杂度为O(n),空间复杂度为O(1)。
主要思路:用T[i][0]表示第i天,库存为0时的最大利润;T[i][1]表示第i天,库存为1时的最大利润。
状态转移方程:
T[i][0] = max(T[i-1][0], T[i-1][1] + V(i))
T[i][1] = max(T[i-1][1], T[i-1][0] - V(i))
具体解释可以看这里,内有此类型问题的一系列分析。
https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/discuss/108870
代码
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
int T_ik0 = 0, T_ik1 = -99999; //初始化状态
for (int price : prices) {
int temp = T_ik0;
T_ik0 = max(T_ik0, T_ik1 + price - fee);
T_ik1 = max(T_ik1, temp - price);
}
return T_ik0;
}
};
总结
1、正确的状态定义
2、从前往后求解,可以减少空间复杂度