[LeetCode] dynamic programming, annihilate "the best time to stock trading" problem!

LeetCode about the best time to stock trading to a total of six topics:

In the best time ⅠⅡ [LeetCode] buying and selling stocks in, Jungle peaks and valleys using two simple methods to solve the problem. Then the remaining four to the topic of how to solve it?

In fact, the subject contrast to 6, subject of the request are similar, particularly to limit the difference frequency and stock trading k

The best time to buy and sell stocks subject contrast LeetCode
topic K limit the number of transactions special requirements
121. The best time to stock trading k=1 no
122. The best time to buy and sell stocks Ⅱ k=+∞ no
123. The best time to buy and sell stocks Ⅲ k=2 no
309. The best time to buy and sell stocks with freezing period k=+∞ Once a transaction is completed, the next day unable to trade
188. The best time to buy and sell stocks Ⅳ k = variable  
714. The best time to buy and sell stocks with fee k=+∞ Each transaction including fees

In this paper, Jungle will use dynamic programming , annihilate the above six questions.

About Dynamic Programming:

1. Topic analysis

We 188. The best time to buy and sell stocks Ⅳ requirements, for example, the title of the number of transactions for the variables k, k = 1 is the first 121 title, k = 2, ie 124 title, k = + ∞ ie 122 questions in 122 topics the limit is the freezing of 309 questions, plus fees became 714 questions.

Of course, the default limits are:

  • You can not perform multiple transactions, the stock must be sold out before before buying again
  • To profit, selling price can not be lower than the purchase price

Given an array of prices, its first  i  element is a given stock at the  price day.

Buying and selling stocks, generally have the following states: short positions (Empty), buy (Buy), hold (Hold) and sell (Sell). But sum up, in fact, a total of just two states: hold (Hold) and short positions (Empty) .

 How these two states is to change it? (Dynamic programming mentioned recursive relationship , that is the state transition equation )

  • The first day i hold the stock, may be the first day i-1 stock holdings, it could be the first day i bought stock
  • I-sky positions, may be the first i-1 day short positions, it could be sold the stock on day i

Already mentioned above two state variables, namely the number of days and i state stock holdings . Do not forget there is a topic transactions k restrictions. We can assume that, at the time to buy stocks on the use of a number of transactions (also can be counted upon to sell). Then the state of transactions is how to change it?

  • I assume that the first day has been traded k times:
    • It may be the cut-off day-1 i have been traded k times
    • It may be off the first day of trading of the i-1 k-1 times a day i bought stock

Therefore, we define a three-dimensional array DP, three dimensions represent the three state variables, as shown below:

(Based on a three-step problem-solving dynamic programming dynamic programming solution to a problem and LeetCode analysis [LeetCode] ):

Meaning (1) explicitly represented array element

As shown above, dp [i] [k] [0] represents the first day i do not hold the largest share of the profits, as of the first day i transactions k; dp [i] [k] [1] represents the i-day holding the maximum profit shares, as of the first day i transactions k.

The last day, it must be held no shares, the final demand is dp [last] [k] [0].

(2) to find recurrence relations, be sure to consider the special case of recurrence relations

Recursion relations analysis is the state of the conversion relation, the above analysis can be obtained:

dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1]+prices[i])

dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0]-prices[i])

(3)数组初始化

需要明确初始状态:

  • 如果在第1天(i=0)买入:dp[0][k][1] = -prices[0]
  • 如果在第i天(i=0)不买入:dp[0][k][0] = 0

但k是从1开始的,k=0代表根本不会有交易,所以k=0时利润为0:

  • dp[i][0][0] = 0
  • dp[i][0][1] = 0

(4)通用代码

int maxProfit(vector<int>&prices){
	int len = prices.size();
	if (len <= 1){
		return 0;
	}
	// 限制的交易次数
	int max_k;
	// 声明数组dp[len][k+1][2]
	vector<vector<vector<int>>>dp(len, vector<vector<int>>(max_k + 1, vector<int>(2, 0)));
	
	// 初始化
	for (int i = 0; i < len; i++){
		dp[i][0][0] = 0;
		dp[i][0][1] = 0;
		dp[i][1][0] = 0;
		dp[i][1][1] = -prices[0];
	}

	// 状态转移通式
	for (int i = 1; i < len; i++){
		for (int k = 1; k <= max_k; k++){
			dp[i][k][0] = max(dp[i][k][0], dp[i - 1][k][1] + prices[i]);
			dp[i][k][1] = max(dp[i][k][1], dp[i - 1][k-1][0] - prices[i]);
		}
	}
	return dp[len - 1][max_k][0];
}

2.求解股票买卖的最佳时机

121. 买卖股票的最佳时机

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

注意你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。


示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

本题只能交易一次,即k=1。由于k=0的时候最大利润为0,所以状态转移方程如下:

dp[i][1][0] = max(dp[i-1][1][0],dp[i-1][1][1]+prices[i]);
dp[i][1][1] = max(dp[i-1][1][1],dp[i-1][0][0]-prices[i]);

上述代码通式可以简化,因为确认k=1,所以可以不要k的维度:

int maxProfit(vector<int>& prices){
	int len = prices.size();
	if (len <= 1){
		return 0;
	}
	int **dp = new int*[len];
	for (int i = 0; i<len; i++){
		dp[i] = new int[2];
	}
	dp[0][0] = 0;
	dp[0][1] = -prices[0];
	int maxProfit = dp[0][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], -prices[i]);
		if (dp[i][0]>maxProfit){
			maxProfit = dp[i][0];
		}
	}
	return maxProfit;
}

122. 买卖股票的最佳时机 II

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 随后,在第 4 天(股票价格 = 3)买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。


示例 2:

输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。


示例 3:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

本题k=+∞,通用代码里的第二个for循环怎么办呢?其实这时候跟k的状态也无关了,因为可以认为k和k-1是一样的,代码如下:

int maxProfit(vector<int>&prices){
	int len = prices.size();
	if (len <= 1){
		return 0;
	}
	// 声明数组dp[len][k+1][2]
	vector<vector<int>>dp(len, vector<int>(2, 0));

	// 初始化
	dp[0][0] = 0;
	dp[0][1] = -prices[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][0];
}

123. 买卖股票的最佳时机 III

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [3,3,5,0,0,3,1,4]
输出: 6
解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 .随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。


示例 2:

输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。   
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。


示例 3:

输入: [7,6,4,3,1] 
输出: 0 
解释: 在这个情况下, 没有交易完成, 所以最大利润为 0。

本题k=2,代码如下:

int maxProfit(vector<int>& prices) {
	int len = prices.size();
	if (len <= 1){
		return 0;
	}
	vector<vector<vector<int>>>dp(len, vector<vector<int>>(3, vector<int>(2, 0)));
	dp[0][0][0] = 0;
	dp[0][0][1] = 0;
	dp[0][1][0] = 0;
	dp[0][1][1] = -prices[0];
	dp[0][2][0] = 0;
	dp[0][2][1] = -prices[0];
	for (int i = 1; i < len; i++){
		for (int k = 1; k <= 2; k++){
			dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
			dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]);
		}
	}
	return dp[len - 1][2][0];
}

188. 买卖股票的最佳时机 IV 

 https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。

注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [2,4,1], k = 2
输出: 2
解释: 在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
示例 2:

输入: [3,2,6,5,0,3], k = 2
输出: 7
解释: 在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。
     随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。

本题k为一个变量,作为参数输入函数,那么可以直接使用上面的通式。但是在提交的时候会出现超时的现象。这是因为一次买卖至少需要2天,那么在len天里,最多可以交易len/2次,当k>len/2时,其实k就不再有实际约束作用,此时的解等同于k为无穷大的时候的解。

int maxProfit(int k, vector<int>& prices) {
        int len = prices.size();
        if (len <= 1 || k == 0 ){
            return 0;
        }
        if(k>len/2){
            return maxProfit(k为无穷大的时候的解);
        }
        vector<vector<vector<int>>>dp(len, vector<vector<int>>(k+1, vector<int>(2,0)));
        dp[0][0][0] = 0;
        dp[0][0][1] = 0;
        for (int j = 1; j <= k; j++){
            dp[0][j][0] = 0;
            dp[0][j][1] = -prices[0];
        }
        for (int i = 1; i < len; i++){
            for (int j = 1; j <= k; j++){
                dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
                dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j-1][0] - prices[i]);
            }
        }
        return dp[len - 1][k][0];
    }

 309. 最佳买卖股票时机含冷冻期

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:

输入: [1,2,3,0,2]
输出: 3 
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

本题限制了冷冻期,那么状态转移方程需要做修改:

dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i]);
dp[i][1] = max(dp[i-1][1],dp[i-2][0]-prices[i]);

 其余等同于k为无穷大的时候的解:

int maxProfit(vector<int>& prices) {
	int len = prices.size();
	if (len <= 1){
		return 0;
	}
	int **dp = new int*[len];
	for (int i = 0; i<len; i++){
		dp[i] = new int[2];
	}
	dp[0][0] = 0;
	dp[0][1] = -prices[0];
	dp[1][0] = max(dp[0][0], dp[0][1] + prices[1]);
	dp[1][1] = max(dp[0][1], dp[0][0] - prices[1]);
	for (int i = 2; 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 - 2][0] - prices[i]);
	}
	return dp[len - 1][0];
}

714. 买卖股票的最佳时机含手续费

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/

给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每次交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

示例 1:

输入: 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.
注意:

0 < prices.length <= 50000.
0 < prices[i] < 50000.
0 <= fee < 50000.

本题k为无穷大,加上了手续费,有什么关系呢?

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]);

 代码如下:

int maxProfit(vector<int>& prices, int fee) {
	int len = prices.size();
	if (len <= 1){
		return 0;
	}
	int **dp = new int*[len];
	for (int i = 0; i<len; i++){
		dp[i] = new int[2];
	}
	dp[0][0] = 0;
	dp[0][1] = -prices[0];
	for (int i = 1; i<len; 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[len - 1][0];
}

就这样,6到股票买卖的最佳时机问题,就这样被解决了!


欢迎关注知乎专栏:Jungle是一个用Qt的工业Robot

欢迎关注Jungle的微信公众号:Jungle笔记

发布了150 篇原创文章 · 获赞 120 · 访问量 10万+

Guess you like

Origin blog.csdn.net/sinat_21107433/article/details/103933028