打家劫舍与买卖股票
- 打家劫舍问题
- 打家劫舍
- 打家劫舍II
- 打家劫舍III
- 买卖股票问题
- 买卖股票的最佳时机
- 买卖股票的最佳时机II
- 买卖股票的最佳时机III
- 买卖股票的最佳时机IV
- 最佳买卖股票时机含冷冻期
- 买卖股票的最佳时机含手续费
- 股票问题总结
打家劫舍问题
给定一个数组,相邻的数字不能取,求能取(偷)到的最大数据和。
打家劫舍
力扣链接
示例 1:
输入:[1,2,3,1]
输出:4
示例 2:
输入:[2,7,9,3,1]
输出:12
注意:
- 0 <= nums.length <= 100
- 0 <= nums[i] <= 400
动态规划五部曲
-
dp数组含义: 考虑下标 i(包括i,只考虑,不一定偷)以内的房屋,最多可以偷窃的金额为dp[i]。
-
确定递推公式:
- 偷当前前数组,则前一个不能偷:
dp[i-2] + nums[i]
- 不偷当前数据,保持考虑偷前一个的最大值:
dp[i-1]
- 取最大值,所以递推公式为:
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
- 偷当前前数组,则前一个不能偷:
-
初始化数组:
dp[0] = 0; dp[1] = max(nums[0], nums[1]);
-
确定遍历顺序: 根据递推公式,顺序遍历
-
举例推导dp数组: 输入[2,7,9,3,1]为例
程序实现:
int rob(vector<int>& nums)
{// dp[i] : 考虑包括下标 i 之前所偷的最大金币 dp[i];//(nums[i]不一定偷,只考虑到)// 结果:dp[nums.size() - 1]// 偷i: dp[i-2] + nums[i]// 不偷i: dp[i-1]// dp[i] = max(dp[i-2] + nums[i], dp[i-1]);//dp[0] = nums[0];//dp[1] = max(nums[0],nums[1]);if(nums.size() == 1)return nums[0];vector<int> dp(nums.size(),0);dp[0] = nums[0];dp[1] = max(nums[0],nums[1]);for(int i = 2; i < nums.size();i++)dp[i] = max(dp[i-2] + nums[i], dp[i-1]);return dp[nums.size() - 1];
}
打家劫舍II
力扣链接
循环数组打家劫舍,即首尾数据算相邻,无法同时偷盗。
示例 :
输入:nums = [2,3,2]
输出:3
解释:不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
提示:
- 1 <= nums.length <= 100
- 0 <= nums[i] <= 1000
对于一个数组,成环的话主要考虑三种情况:
- 情况一:考虑不包含首尾元素
- 情况二:考虑包含首元素,不包含尾元素
- 情况三:考虑包含尾元素,不包含首元素
情况二 和 情况三 都包含了情况一了,所以只考虑情况二和情况三就可以了。
分析到这里,本题其实比较简单了。 剩下的上一题打家劫舍问题完全一样。只需要计算一个区间上的打家窃舍最大值。
程序实现:
class Solution {
public:int rob(vector<int>& nums) {if(nums.size() == 1)return nums[0];// 最后一个数据的下标为 nums.size() - 1int ret1 = robRange(nums, 0, nums.size() - 2);int ret2 = robRange(nums, 1, nums.size() - 1);return max(ret1,ret2);}//计算一个区间上的打家窃舍的最大值int robRange(vector<int>& nums, int start, int end){// dp[i] : 考虑包括下标 i 之前所偷的最大金币 dp[i];//(nums[i]不一定偷,只考虑到)// 结果:dp[end]// 偷i: dp[i-2] + nums[i]// 不偷i: dp[i-1]// dp[i] = max(dp[i-2] + nums[i], dp[i-1]);//dp[start] = nums[start];//dp[start +1 ] = max(nums[start],nums[start+1]);if(start == end)return nums[start];vector<int> dp(nums.size(),0);dp[start] = nums[start];dp[start + 1] = max(nums[start], nums[start+1]);for(int i = start + 2; i <= end; i++)dp[i] = max(dp[i-2] + nums[i], dp[i-1]);return dp[end];}
};
打家劫舍III
力扣原题
二叉树的打家窃舍,相邻节点不能偷。
这道题目算是树形dp的入门题目,如果对二叉树遍历不熟悉,就比较有难度。
本题一定是要后序遍历,因为通过递归函数的返回值来做下一步计算。
与数组的打家窃舍问题一样,关键是要讨论当前节点抢还是不抢
如果抢了当前节点,两个孩子就不能动,如果没抢当前节点,就可以考虑抢左右孩子(这里说的是“考虑”)
递归三部曲为框架,融合动规五部曲:
-
1. 确定递归函数的参数和返回值
这里要求一个节点偷与不偷的两个状态所得到的金钱,那么返回值就是一个长度为2的数组,参数为当前节点,代码如下:
vector<int> robTree(TreeNode* cur)
dp数组以及下标含义: 下标为0记录不偷该节点所得到的的最大金钱,下标为1记录偷该节点所得到的的最大金钱。
-
2. 确定终止条件
在遍历的过程中,如果遇到空节点的话,很明显,无论偷还是不偷都是0,所以就返回,这也相当于dp数组的初始化
if (cur == NULL) return vector<int>{0, 0}
-
3. 确定遍历顺序
使用后序遍历,通过递归函数的返回值来做下一步计算。
通过递归左节点,得到左节点偷与不偷的金钱。
通过递归右节点,得到右节点偷与不偷的金钱。
代码如下:
// 下标0:不偷,下标1:偷 vector<int> left = robTree(cur->left); // 左 vector<int> right = robTree(cur->right); // 右 // 中
-
4. 确定单层递归的逻辑
-
偷当前节点,左右孩子不偷:
val1 = cur->val + left[0] + right[0];
-
不偷当前节点,左右孩子各偷一个大的
val2 = max(leftdp[0],leftdp[1]) + max(rightdp[0], rightdp[1]);
-
最后当前节点的状态就是{val2, val1}; 即:{不偷当前节点得到的最大金钱,偷当前节点得到的最大金钱}
-
-
5. 举例推导dp数组
最后头结点就是 取下标0 和 下标1的最大值就是偷得的最大金钱
程序实现:
int rob(TreeNode* root)
{//树形dp引入// dp[2] : 保存当前节点偷与不偷,dp[0] : 不偷当前节点最大金额// 由于递归遍历二叉树,当前层的dp保存当前节点的状态vector<int> ret = robTree(root);return max(ret[0], ret[1]);
}
// 长度为2的数组,0:不偷,1:偷
vector<int> robTree(TreeNode* cur)
{// 后续遍历二叉树if(cur == NULL)return vector<int> {0,0};//向左右遍历vector<int> leftdp = robTree(cur->left);vector<int> rightdp = robTree(cur->right);// 中间节点偷 左右不偷int value1 = cur->val + leftdp[0] + rightdp[0];// 中间节点不偷 偷左右 int value2 = max(leftdp[0],leftdp[1]) + max(rightdp[0], rightdp[1]);return {value2, value1};
}
买卖股票问题
-
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格,通过一些列的买卖规则,就散相关所得最多现金。
-
dp[i][j]:表示第 i 天某种状态 j 所得最多现金
-
例如:
- dp[i][0] 表示第i天持有股票所得最多现金
- dp[i][1] 表示第i天不持有股票所得最多现金
-
注意: 这里是第 i 天持有股票和不持有股票,不代表一定是第 i 天买入,也可以是保持前一天状态。
买卖股票的最佳时机
力扣链接
选择某一天买入这只股票,并选择在未来的某一个不同的日子卖出该股票,计算你所能获取的最大利润,如果你不能获取任何利润,返回 0 。
示例 1:
输入:[7,1,5,3,6,4]
输出:5
贪心算法: 因为股票就买卖一次,那么贪心的想法很自然就是取最左最小值,取最右最大值,那么得到的差值就是最大利润。
程序实现:
int maxProfit(vector<int>& prices)
{//[7,1,5,3,6,4]int result = 0;int low = INT_MAX;for(int i = 0; i < prices.size(); i++){low = min(low, prices[i]); // 获取从左开始的最小值result = max(result, prices[i] - low); // 获取最大利益}return result;
}
动态规划
动态规划五部曲
1. dp数组含义: dp[i][0] 表示第i天持有股票所得最多现金 ,dp[i][1] 表示第i天不持有股票所得最多现金,注意这里是持有,不是买入。
2. 确定递推公式:
-
对于当天持有股票有2种情况,当天买入和保持上一天情况
- 当天买入:
dp[i][0] = -prices[i]
,只买卖一次,因此一定是第一次买入 - 保持上一天的情况:
dp[i][0] = dp[i-1][0]
dp[i][0] = max(dp[i-1][0], -prices[i]);
- 当天买入:
-
对于当天不持有股票也有2种情况,当天卖出和保持上一天情况
- 当天卖出:前一天持有的最大金额 + 卖出获取的金额
dp[i][1] = dp[i-1][0] + prices[i]
- 保持:
dp[i][1] = dp[i-1][1];
dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i]);
- 当天卖出:前一天持有的最大金额 + 卖出获取的金额
-
3. 初始化数组: 根据递推公式,需要对dp[0][0],dp[0][1]初始化
dp[0][0] = -prices[0]; // 第一天就卖出 手上利润变成-prices[0]元了 dp[0][1] = 0; // 根据递推公式, dp[0][1] = 0
-
5. 举例推导dp数组:
dp[5][1]即为最终结果:本题中不持有股票状态所得金钱一定比持有股票状态得到的多!
程序实现:
// 动规
int maxProfit(vector<int>& prices)
{// dp[i][0] 表示第i天持有股票所得最多现金// 第i天买入股票:-prices[i]// 保持不变: dp[i-1][0] // dp[i][1] 表示第i天不持有股票所得最多现金// 第i天卖出股票: dp[i - 1][0] + prices[i] // 保持不变: dp[i-1][1]int len = prices.size();if(len <= 1)return 0;vector<vector<int>> dp(len, vector<int>(2));dp[0][0] = -prices[0];dp[0][1] = 0;for(int i = 1; i < len; i++){dp[i][0] = max(dp[i-1][0], -prices[i]);dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i]);}return dp[len-1][1];
}
买卖股票的最佳时机II
- 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
- 你可以尽可能地完成更多的交易(多次买卖一支股票)。
- 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
- 没有交易返回0
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
示例 2:
输入: [1,2,3,4,5]
输出: 4
贪心:本题在贪心章节提及过,一次最大收益的买卖可以分割为每天的收益,因此贪心累加每天的正收益即可。
动规: 该题在买卖股票Ⅰ的题目上进行改进,将只可买卖一次改为可以买卖多次, 因此只需修改持有股票状态下的当天买入股票的表达式:
dp[i][0] = dp[i-1][1] - peices[i]
程序实现:
// 动规
int maxProfit(vector<int>& prices)
{// dp[i][0] 表示第i天持有股票所得最多现金// 第i天买入股票:dp[i-1][1] - prices[i] // 保持不变: dp[i-1][0] // dp[i][1] 表示第i天不持有股票所得最多现金// 第i天卖出股票: dp[i - 1][0] + prices[i] // 保持不变: dp[i-1][1]int len = prices.size();if(len <= 1)return 0;vector<vector<int>> dp(len, vector<int>(2));dp[0][0] = -prices[0];dp[0][1] = 0;for(int i = 1; i < len; 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]);}return dp[len-1][1];
}
买卖股票的最佳时机III
力扣链接
-
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
-
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
-
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
本题在上述两个题上进行改进,设置买卖次数最多为2次(0-2次均可)
1. 确定dp数组
因为本题可以买卖2次,因此设置5种状态,分别为:
状态 | 表示 |
---|---|
0.没有操作 | dp[i][0] |
1.第一次持有股票 | dp[i][1] |
2.第一次不持有股票 | dp[i][2] |
3.第二次持有股票 | dp[i][3] |
4.第二次不持有股票 | dp[i][4] |
2. 确定递推公式
-
状态0:保持上一天的状态
dp[i][0] = dp[i-1][0]
-
状态1:第一次持有股票(当天买入 / 保持上一天已持股)
- 当天买入:上一天的最大金额 - 当前买股票费用,即
dp[i-1][0] - prices[i]
- 保持上一天已持股:
dp[i-1][1]
- 所以递推公式为:
dp[i][1] = max(dp[i-1][0] - prices[i], dp[i-1][1])
- 当天买入:上一天的最大金额 - 当前买股票费用,即
-
状态2:第一次不持有股票(当天卖出 / 保持上一天不持股)
- 当天卖出:上一天持股最大金额 + 当前卖出的收益,即
dp[i-1][1] + prices[i]
- 保持上一天未持股:
dp[i-1][2]
- 所以递推公式为:
dp[i][2] = max(dp[i-1][1] + prices[i], dp[i-1][2])
- 当天卖出:上一天持股最大金额 + 当前卖出的收益,即
-
状态3:第二次持有股票(当天买入 / 保持上一天已持股)
- 同理状态1,递推公式为:
dp[i][3] = max(dp[i-1][2] - prices[i], dp[i-1][3]);
- 同理状态1,递推公式为:
-
状态4:第二次未持有股票(当天卖出 / 保持上一天未持股)
- 同理状态2,递推公式为:
dp[i][4] = max(dp[i-1][3] + prices[i], dp[i-1][4]);
- 同理状态2,递推公式为:
3. 初始化数组:
dp[0][0] = 0; // 第0天没有操作
dp[0][1] = -prices[0]; // 第0天 第一次买入的操作
dp[0][2] = 0; // 理解成当天买入 当天卖出
dp[0][3] = -prices[0]; // 当于第0天第一次买入了,第一次卖出了,然后再买入一次,手上没有现金
dp[0][4] = 0; // 同理dp[0][2]
5. 举例推导dp数组: 以输入[1,2,3,4,5]为例
程序实现:
// 动规
int maxProfit(vector<int>& prices)
{// 一天有五个操作/*dp[i][j]表示第i天状态j后所剩的最大现金0. 没有操作 (其实我们也可以不设置这个状态)dp[i-1][0]1. 第一次持有股票 (1) 第 i 天买入:dp[i-1][0] - prices[i](2)第 i 之前就买入,第 i 天只是保持不动 dp[i-1][1]所以:dp[i][1] = max(dp[i-1][0] - prices[i], dp[i-1][1])2. 第一次不持有股票(1) 第 i 天卖出:dp[i-1][1] + prices[i](2) 第 i 天之前卖出,第 i 天只是不变:dp[i-1][2]所以 dp[i][2] = max(dp[i-1][1] + prices[i], dp[i-1][2])3. 第二次持有股票同理状态1:dp[i][3] = max(dp[i-1][2] - prices[i], dp[i-1][3])4. 第二次不持有股票同理状态2:dp[i][4] = max(dp[i-1][3] + prices[i], dp[i-1][4])*/int len = prices.size();if(len <= 1)return 0;vector<vector<int>> dp(len, vector<int>(5));dp[0][0] = 0;dp[0][1] = -prices[0];dp[0][2] = 0;dp[0][3] = -prices[0];dp[0][4] = 0;for(int i = 1; i < len; i++){dp[i][0] = dp[i-1][0];dp[i][1] = max(dp[i-1][0] - prices[i], dp[i-1][1]);dp[i][2] = max(dp[i-1][1] + prices[i], dp[i-1][2]);dp[i][3] = max(dp[i-1][2] - prices[i], dp[i-1][3]);dp[i][4] = max(dp[i-1][3] + prices[i], dp[i-1][4]);}return max(dp[len-1][2],dp[len-1][4]);
}
买卖股票的最佳时机IV
力扣原题
- 给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
- 设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
- 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:k = 2, prices = [2,4,1]
输出:2
示例 2:
输入:k = 2, prices = [3,2,6,5,0,3]
输出:7
本题是对上题(买卖股票最佳时机III)的更一般化,从最多买卖 k 次延伸到最多买卖 k次,引入k,就是将递归公式一般化
1. dp数组含义:
状态0:不操作
状态1:第一次持有股票 状态2:第一次未持有股票
状态3:第二次持有股票 状态4:第二次未持有股票
.……
状态2 * k - 1:第 k 次持有股票 状态2 * k:第 k 次未持有股票
2. 确定递推公式:
将上一题的递推公式一般化,通过找规律,上一题递推公式如下:
k = 2
保持状态
dp[i][0] = dp[i-1][0];
第一次 买卖
dp[i][1] = max(dp[i-1][0] - prices[i], dp[i-1][1]);
dp[i][2] = max(dp[i-1][1] + prices[i], dp[i-1][2]);
第二次买卖
dp[i][3] = max(dp[i-1][2] - prices[i], dp[i-1][3]);
dp[i][4] = max(dp[i-1][3] + prices[i], dp[i-1][4]);
引入k,将公式推广一般化:
for(int i = 1; i < len; i++)
{dp[i][0] = dp[i-1][0];// 1 3 5for(int j = 1; j < 2 * k; j += 2){dp[i][j] = max(dp[i-1][j-1] - prices[i], dp[i-1][j]); // 买入dp[i][j+1] = max(dp[i-1][j] + prices[i], dp[i-1][j+1]); // 卖出}
}
3. 初始化数组:
第0天买入后所剩余的现金均为 -prices[0],卖出后的现金剩余均为0
vector<vector<int>> dp(len, vector<int>(2 * k + 1, 0));
//初始化
for(int i = 1; i < 2 * k; i += 2)dp[0][i] = -prices[0];
5. 举例推导dp数组: 以输入[1,2,3,4,5],k=2为例
程序实现:
// 动规
int maxProfit(int k, vector<int>& prices)
{// 由于最多 k 次,一天相当于有 2 * k + 1 个操作/*dp[i][j]表示第i天状态j后所剩的最大现金0. 没有操作 (其实我们也可以不设置这个状态)dp[i-1][0]1. 第一次持有股票 (1) 第 i 天买入:dp[i-1][0] - prices[i](2)第 i 之前就买入,第 i 天只是保持不动 dp[i-1][1]所以:dp[i][1] = max(dp[i-1][0] - prices[i], dp[i-1][1])2. 第一次不持有股票(1) 第 i 天卖出:dp[i-1][1] + prices[i](2) 第 i 天之前卖出,第 i 天只是不变:dp[i-1][2]所以 dp[i][2] = max(dp[i-1][1] + prices[i], dp[i-1][2])3. 第二次持有股票同理状态1:dp[i][3] = max(dp[i-1][2] - prices[i], dp[i-1][3])4. 第二次不持有股票同理状态2:dp[i][4] = max(dp[i-1][3] + prices[i], dp[i-1][4])......// j = 1 3 5第 k 次持有股票: dp[i][j] = max(dp[i-1][j-1] - prices[i], dp[i-1][j]);第 k 次不持有股票: dp[i][j] = max(dp[i-1][j-1] + prices[i], dp[i-1][j]);*/int len = prices.size();if(len <= 1)return 0;vector<vector<int>> dp(len, vector<int>(2 * k + 1, 0));//初始化for(int i = 1; i < 2 * k; i += 2)dp[0][i] = -prices[0];//递推公式 k = 2for(int i = 1; i < len; i++){dp[i][0] = dp[i-1][0];// 1 3 5for(int j = 1; j < 2 * k; j += 2){dp[i][j] = max(dp[i-1][j-1] - prices[i], dp[i-1][j]); // 买入dp[i][j+1] = max(dp[i-1][j] + prices[i], dp[i-1][j+1]); // 卖出}}return dp[len-1][2 * k];
}
最佳买卖股票时机含冷冻期
力扣链接
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
- 不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
- 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
分析
- 本题相对于 买卖股票的最佳时机II 多出了一个冷冻期,
- 因此需要对之前未持有股票的状态进行分解为2个状态:保持股票卖出状态和当日卖出股票的状态
- 同时还需一个冷冻期的状态
1. dp数组含义:
状态 | 表示 | 含义 |
---|---|---|
状态0 | dp[i][0] | 表示第 i 天 持有股票的最多现金 |
状态1 | dp[i][1] | 表示第 i 天 保持股票卖出的最多现金 |
状态2 | dp[i][2] | 表示第 i 天 股票卖出后最多现金 |
状态3 | dp[i][3] | 表示第 i 天为 冷冻期时的最多现金 |
2. 确定递推公式:
-
状态0:持有股票(当天买入 / 保持上一天已经持有)
-
保持上一天已持有:
dp[i-1][0]
-
第 i 天买入股票,也有两种情况
- 前一天是冷冻期:
dp[i-1][3] - prices[i]
- 前一天是保持股票卖出状态:
dp[i-1][1] - prices[i]
- 前一天是冷冻期:
-
所以
dp[0][i] = max(dp[i - 1][0], dp[i - 1][3] - prices[i], dp[i - 1][1] - prices[i]);
-
-
状态1: 保持股票卖出的状态
- 保持前一天的卖出状态:
dp[i-1][1]
- 冷冻期后一天:
dp[i-1][3]
- 所以,递推公式为:
dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
- 保持前一天的卖出状态:
-
状态2:表示第 i 天卖出股票的状态
- 前一天肯定持股,所以递推公式为:
dp[i][2] = dp[i - 1][0] + prices[i];
- 前一天肯定持股,所以递推公式为:
-
状态3:冷冻期状态
- 昨天卖股票,所以递推公式为:
p[i][3] = dp[i-1][2]
- 昨天卖股票,所以递推公式为:
3. dp数组如何初始化
dp[0][0] = -prices[0]; // 当天买入股票。
dp[0][1] = 0;
dp[0][2] = 0;
dp[0][3] = 0;
5. 举例推导dp数组: 以 [1,2,3,0,2] 为例,dp数组如下:
程序实现:
// 动规
int maxProfit(vector<int>& prices)
{// 状态0:// dp[i][0] 表示第i天持有股票所得最多现金// 保持不变: dp[i-1][0] // 第i天买入股票:// 冷冻期后一天买入:dp[i-1][3] - prices[i]// 保持买入股票状态:dp[i-1][1] - prices[i]// 状态1:// dp[i][1] 保持股票卖出的状态// 保持不变:dp[i-1][1]// 冷冻期后一天也是保持卖出的状态,前一天是冷冻期,dp[i-1][3]// 状态2:// dp[i][2] 表示第i天卖出股票的状态,前一天肯定持股// 第i天卖出股票: dp[i][2] = dp[i-1][0] + prices[i] // 状态3:// dp[i][3]: 冷冻期//冷冻期前一天肯定是卖出股票 dp[i][3] = dp[i-1][2]int len = prices.size();if(len <= 1)return 0;vector<vector<int>> dp(len, vector<int>(4));dp[0][0] = -prices[0];dp[0][1] = 0;dp[0][2] = 0;dp[0][3] = 0;for(int i = 1; i < len; i++){dp[i][0] = max(dp[i-1][0], max(dp[i-1][1] - prices[i], dp[i-1][3] - prices[i]));dp[i][1] = max(dp[i-1][3], dp[i-1][1]);dp[i][2] = dp[i-1][0] + prices[i];dp[i][3] = dp[i-1][2]; }return max(dp[len-1][1], max(dp[len-1][2], dp[len-1][3]));
}
买卖股票的最佳时机含手续费
力扣链接
-
给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。
-
可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。返回获得利润的最大值。
-
注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
示例 1:
输入: prices = [1, 3, 2, 8, 4, 9], fee = 2
输出: 8
本题在 买卖股票的最佳时机II 的题目上进行更改,只需将每次在第i天不持有股票所得最多现金状态下,每次卖出股票时候扣除手续费即可,表示结束一轮交易。
dp[i][1] = dp[i-1][0] + prices[i] - fee
确定dp数组含义
状态0:
- dp[i][0] 表示第 i 天持有股票所得最多现金
- 保持不变:
dp[i-1][0]
- 第i天买入股票:
dp[i-1][1] - prices[i]
状态1:
- dp[i][1] 表示第i天不持有股票所得最多现金
- 保持不变:
dp[i-1][1]
- 卖出:
dp[i][1] = dp[i-1][0] + prices[i] - fee
程序实现:
// 动规
int maxProfit(vector<int>& prices, int fee)
{// 状态0:// dp[i][0] 表示第i天持有股票所得最多现金// 保持不变: dp[i-1][0] // 第i天买入股票:dp[i-1][1] - prices[i]// 状态1:// dp[i][1] 表示第i天不持有股票所得最多现金// 保持不变:dp[i-1][1]// 卖出:dp[i][1] = dp[i-1][0] + prices[i] - feeint len = prices.size();if(len <= 1)return 0;vector<vector<int>> dp(len, vector<int>(2));dp[0][0] = -prices[0];dp[0][1] = 0;for(int i = 1; i < len; 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[len-1][0], dp[len-1][1]);
}
股票问题总结
股票问题总结