题目1----121. 买卖股票的最佳时机I:
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
解答:
限制只能买入卖出一次
DP1:
buy[i]记录截止到第i天是买入的状态的最小花费(值为负数)
1 class Solution { 2 public: 3 int maxProfit(vector<int>& prices) { 4 if(prices.size()<2){ 5 return 0; 6 } 7 int n=prices.size(),res=0; 8 vector<int> buy(n,0); 9 buy[0]=-prices[0]; 10 for(int i=1;i<n;++i){ 11 res=max(buy[i-1]+prices[i],res); 12 buy[i]=max(-prices[i],buy[i-1]); 13 } 14 return res; 15 } 16 };
严格来说这个不算dp,计算第i天的情况时,只用到了buy[i-1]的数据。。所以前面保存的数据是没有意义的。
DP2:
dp[i]记录第i天当天卖出的最大利润,则最大利润一定等于之前某天买今天卖。
i):首先可以昨天买今天卖。
ii):还可以之前某天买今天卖。dp[i-1]等于昨天之前买入,i-1天卖出的最大利润,那么i-1天不卖,改为第i天卖也可以得到一个利润。
两个利润取最大。
1 class Solution { 2 public: 3 int maxProfit(vector<int>& prices) { 4 if(prices.size()<2){ 5 return 0; 6 } 7 int n=prices.size(); 8 vector<int> dp(n,0); 9 dp[0]=0; 10 int res=0; 11 for(int i=1;i<n;++i){ 12 dp[i]=max(0,prices[i]-prices[i-1]); 13 dp[i]=max(dp[i],dp[i-1]-prices[i-1]+prices[i]); 14 res=max(res,dp[i]); 15 } 16 return res; 17 } 18 };
遍历:
和DP一样都是O(N)时间,和第一种dp一样想法,只是不用记录dp数组了。毕竟考察i的时候,只需要buy[i-1]的数据。
思路:最高利润出现在:买入为价格最低时,卖出为买入之后价格最高时。
故当更新最小值价格时,之前的最大价格要舍弃,从当前索引继续考察。
1 class Solution: 2 def maxProfit(self, prices: List[int]) -> int: 3 l=len(prices) 4 if l<2: 5 return 0 6 _min,_max=0,0 7 i=0 8 res=0 9 while i<l: 10 if prices[i]<prices[_min]: 11 _min=i 12 _max=i 13 if prices[i]>prices[_max]: 14 _max=i 15 res=max(res,prices[_max]-prices[_min]) 16 i+=1 17 return res
题目2----122. 买卖股票的最佳时机II:
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
解答:
无限制次数的买入卖出
1 class Solution { 2 public: 3 int maxProfit(vector<int>& prices) { 4 int n=prices.size(); 5 if(n<2){return 0;} 6 vector<int> buy(n,0),sell(n,0); 7 buy[0]=-prices[0]; 8 for(int i=1;i<n;++i){ 9 sell[i]=max(sell[i-1],prices[i]+buy[i-1]);//之前卖今天不卖 or 之前买今天卖 10 buy[i]=max(buy[i-1],sell[i-1]-prices[i]);//之前买今天不买 or 之前卖了今天买 11 } 12 return sell[n-1]; 13 } 14 };
题目3----123. 买卖股票的最佳时机III:
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
解答:
限制最多两次买入卖出
定义了4个dp数组,分别是
buy1[i]:第i天是第一次买入的状态
sell[i]:第i天是第一次卖出的状态
buy2、sell2同理。
由于一开始还不能第二次买和第二次卖,所以赋值为负无穷。
1 class Solution { 2 public: 3 #define inf INT_MIN 4 int maxProfit(vector<int>& prices) { 5 int n=prices.size(); 6 if(n<2){return 0;} 7 vector<int> buy1(n,0),sell1(n,0),buy2(n,inf),sell2(n,inf); 8 buy1[0]=-prices[0]; 9 for(int i=1;i<n;++i){ 11 sell1[i]=max(prices[i]+buy1[i-1],sell1[i-1]); 12 buy1[i]=max(-prices[i],buy1[i-1]); 13 sell2[i]=max(buy2[i-1]+prices[i],sell2[i-1]); 14 buy2[i]=max(buy2[i-1],sell1[i-1]-prices[i]); 16 } 17 return max(0,max(sell2[n-1],sell1[n-1])); 18 } 19 };
方法2:先计算1次买卖最大的利润sell1[i]。再计算从第i天开始再买卖一次最大的利润sell2[i]。
第一次买卖是从前向后求(因为左侧是固定的),第二次买卖是从后向前求(因为右侧是固定的)
1 class Solution { 2 public: 3 #define inf INT_MIN 4 int maxProfit(vector<int>& prices) { 5 int n=prices.size(); 6 if(n<2){return 0;} 7 vector<int> sell1(n,0),sell2(n,0); 8 int min_buy=-prices[0]; 9 for(int i=1;i<n;++i){ 10 sell1[i]=max(sell1[i-1],prices[i]+min_buy); 11 min_buy=max(min_buy,-prices[i]); 12 } 13 int res=max(0,sell1[n-1]); 14 int max_sell=prices[n-1]; 15 for(int i=n-2;i>=0;--i){ 16 sell2[i]=max(sell2[i+1],max_sell-prices[i]); 17 max_sell=max(max_sell,prices[i]); 18 res=max(res,sell1[i]+sell2[i]); 19 } 20 return res; 21 } 22 };
之前写过的python版本,以供参考:
1 class Solution: 2 def maxProfit(self, prices) -> int: 3 l=len(prices) 4 if l<2: 5 return 0 6 res=0 7 #先算一个dp1数组 8 #dp1[i]表示截止到第i-1天只进行一次买卖的最大利润 9 dp1=[0 for i in range(l)] 10 max_price,min_price=prices[0],prices[0] 11 for i in range(1,l): 12 dp1[i]=max(dp1[i-1],prices[i]-min_price) 13 #对于第i天来说,1.如果当天卖:最大利润即当前卖出价格 14 #减去之前的最小买入价格,2如果不卖:最大利润和前一天的 15 #最大利润相同 16 min_price=min(min_price,prices[i]) #更新当前最小买入价格 17 max_price=max(max_price,prices[i]) #更新当前最大卖出价格 18 #对于任意k,dp1[k]表示k卖出的最大利润, 19 #那么需要求剩下k+1到n-1的最大利润 20 #倒着求,因为右边界不变始终为l-1,左边界在变化 21 #dp2[i]表示从i开始到最后只进行一次买卖的最大利润 22 res=dp1[-1] 23 # print(res) 24 dp2=[0 for i in range(l)] 25 max_price=prices[-1] 26 for i in range(l-2,-1,-1): 27 dp2[i]=max(dp2[i+1],max_price-prices[i]) 28 #对于第i天,1.若当天买,则最大利润即之后的最大卖出价格减去 29 #当前买入价格,2.若当天不买,最大利润和后一天的最大利润相同 30 max_price=max(max_price,prices[i]) #更新当前最大卖出价格 31 res=max(res,dp1[i-1]+dp2[i]) if i>=1 else max(res,dp2[i]) 32 # print(dp1) 33 # print(dp2) 34 return res
后来发现其实不需要存储dp数组,只需要一个变量记录上一次的状态就行,但我懒,就不写了,反正内存大任性
题目4----188. 买卖股票的最佳时机IV:
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
这道题限制买卖次数最大为k,是一个变量。
设二维dp数组。dp[i][j]表示截止到i最多完成j笔交易的最大利润。
截止第i天,最大利润首先可以是之前的最大利润,即之前就完成了j笔交易,第i天不卖。
当然第i天也可以卖,那么本拨交易的买入最晚也得是i-1天,那么i-2天之前(包含i-2天)就必须要完成j-1笔交易。
用一个max值表示截止到前一天完成j-1笔交易、并且是待卖出状态的最大利润。
递推方程为:dp[i][j]=max(dp[i-1][j],max+prices[i])
另外每次循环中要更新max的值。
另外有用例k给的无限大,那么申请dp数组时会爆栈。需要判断一下k和价格数量的关系,如果k太大,转化为上面第2题的无限次的买卖股票问题。
前面的问题1和问题3只是这道题的特殊情况,代码直接复制过去就可以运行。
1 class Solution { 2 public: 3 int maxProfit(int k, vector<int>& prices) { 4 if(prices.empty()){ 5 return 0; 6 } 7 int n=prices.size(); 8 if(2*k<n){ 9 vector<vector<int>> dp(n,vector<int>(k+1,0)); 10 //dp[i][j]表示截止第i天完成最多j次交易得到的最大收益 11 for(int j=1;j<k+1;++j){ 12 int _max=-prices[0]; 13 for(int i=1;i<n;++i){ 14 dp[i][j]=max(dp[i-1][j],_max+prices[i]); 15 _max=max(dp[i-1][j-1]-prices[i],_max); 16 } 17 } 18 return dp.back().back(); 19 } 20 else{//k太大,相当于可以无限次交易 21 vector<int> dp(n,0); 22 //dp[i]表示截止第i+1天得到的最大收益 23 int _max=-prices[0]; 24 for(int i=1;i<n;++i){ 25 dp[i]=max(dp[i-1],_max+prices[i]); 26 _max=max(_max,dp[i]-prices[i]); 27 } 28 return dp.back(); 29 } 30 return 0; 31 } 32 };
题目5----309. 最佳买卖股票时机含冷冻期:
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。?
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
这道题多了一个冷冻期的限制:卖出之后要至少休息一天才能买入
那么理所当然的,多设立一个dp数组以表示冷冻期的状态:
1 class Solution { 2 public: 3 int maxProfit(vector<int>& prices) { 4 if(prices.size()<2){ 5 return 0; 6 } 7 int n=prices.size(); 8 vector<int> buy(n,0),sell(n,0),freeze(n,0); 9 //buy[i]、sell[i]、freeze[i]分别表示第i天 10 //买入、卖出、不买不卖的最大收益(包含买股票花的钱) 11 buy[0]=-prices[0]; 12 int res=0; 13 for(int i=1;i<n;++i){ 14 buy[i]=max(buy[i-1],freeze[i-1]-prices[i]); 15 sell[i]=max(sell[i-1],buy[i-1]+prices[i]); 16 freeze[i]=max(sell[i-1],freeze[i-1]); 17 res=max(sell[i],res); 18 } 19 return res; 20 } 21 };
也可以仿照第四题的解法,用一个max值保存截止前一天待卖出状态的最大利润,不过更新max的时候,要注意冷冻期的要求。所以_max=max(_max,dp[i-2]-prices[i]),即截止i-2天卖出的最大利润,休息一天,第i天买入。之前的题目是_max=max(_max,dp[i-1]-prices[i]),注意二者的区别,虽然只是1和2的数字不同,但却是整道题的关键
1 class Solution { 2 public: 3 int maxProfit(vector<int>& prices) { 4 int n=prices.size(); 5 if(n<2){return 0;} 6 vector<int> sell(n,0); 7 sell[1]=max(0,prices[1]-prices[0]); 8 int _max=max(-prices[0],-prices[1]); 9 for(int i=2;i<n;++i){ 10 sell[i]=max(sell[i-1],_max+prices[i]); 11 _max=max(_max,sell[i-2]-prices[i]); 12 } 13 // for(int x:sell){cout<<x<<" ";}; 14 return sell[n-1]; 15 } 16 };
完结撒花????
明天就要华为机试了,坏运气给??爬
2020-02-19 02:01:09
原文地址:https://www.cnblogs.com/FdWzy/p/12288157.html