【动态规划】(四)动态规划——打家劫舍与买卖股票

打家劫舍问题

给定一个数组,相邻的数字不能取,求能取(偷)到的最大数据和。

打家劫舍

力扣链接

示例 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() - 1
		int 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]);
      
  • 状态4:第二次未持有股票(当天卖出 / 保持上一天未持股)

    • 同理状态2,递推公式为:
      dp[i][4] = max(dp[i-1][3] + prices[i], dp[i-1][4]);
      

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 5
	for(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 = 2
	for(int i = 1; i < len; i++)
	{
		dp[i][0] = dp[i-1][0];
		// 1 3 5
		for(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数组含义:

状态表示含义
状态0dp[i][0]表示第 i 天 持有股票的最多现金
状态1dp[i][1]表示第 i 天 保持股票卖出的最多现金
状态2dp[i][2]表示第 i 天 股票卖出后最多现金
状态3dp[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] - fee
			
	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] - fee);
	}
	
	return max(dp[len-1][0], dp[len-1][1]);
}

股票问题总结

股票问题总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会编程的小江江

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值