1. 前言
今天在leetcode上看到了一道dp解决股票买卖最大利润的问题,联想到了今年美赛的C题,当时对动态规划还是一知半解,也完全没有往这方面去想。故记录一下这题的思路,也是提醒自己要提高对动态规划的理解,事实上许多数模国一论文都有涉及动态规划的内容。
动态规划的内容可以看看之前算法设计的PPT。
或者参考下别人的博客,如动态规划详解_Ashley zhao的博客-CSDN博客_动态规划
2. 题目描述
原题可见力扣
给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
下面是一个例子:
输入: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来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
3. 问题求解
此问的解法在leetcode上都可以看到,这里我顺带着复习下动归的解题步骤。
①:明确优化目标,建立递归方程
②:给定初始条件,自底向上求解各个子问题
③:根据递归方程编程实现,得到优化解
3.1 建立递归方程
为了方便讨论,我们把手续费放到抛售的时候计算。
首先,构建二维数组 res[2][N],N为天数。
其中 res[0][i] 代表第 i 天未持股的情况下,前 i 天的最大收益;res[1][i] 代表第 i 天持股的情况下,前 i 天的最大收益。
这样,原先求 N 天后最大利润的问题转变为求 res[0][N] 与 res[1][N]中的最大值(当然实际上就是res[0][N])。接下来,就可以建立递归方程了。
注意到 res[0][i] 是由前一天的情况转变而来的,分为两种情况:
若第 i-1 天未持股,则 res[0][i] = res[0][i-1];
若第 i-1 天持股,则前 i 天收益等于前 i-1 天收益加上第 i 天买入股票的收益,即 res[0][i] = res[1][i-1] + price[i] - fee。
由于是求最大值,所以 res[0][i] = max(res[0][i-1], res[1][i-1] - price[i])。
类似的,可以得到 res[1][i] = max(res[1][i-1], res[0][i-1] + price[i] - fee)。
3.2 给定初始条件
在第 0 天时,若未持股则收益为0,否则说明买股,收益为 -price[i]。
即 res[0][0] = 0,res[1][0] = -price[i]。
3.3 问题求解
这是leetcode上给出的官方代码(c++),可以参考。
int maxProfit(vector<int>& prices, int fee) {
int n = prices.size();
vector<vector<int>> dp(n, vector<int>(2));
dp[0][0] = 0, dp[0][1] = -prices[0];
for (int i = 1; i < n; ++i) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
}
return dp[n - 1][0];
}
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/solution/mai-mai-gu-piao-de-zui-jia-shi-ji-han-sh-rzlz/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。