LeetCode上的Best Time to Buy and Sell Stock系列题。第一题、第二题、第三题、第四题、第五题、第六题。
第一题:
题目大意:
给定一个数组,第i个元素代表第i天的股票价格。只允许买卖一次,求出最大利润。
思路:
动态规划。buy记录买入价格,初始为prices[0],maxProfit记录利润最大值,遍历数组,用当前价格减去买入价格,并与最大利润值比较并更新最大利润值。如果当前价格小于买入价格就更新买入价格。
代码:
class Solution { public: int maxProfit(vector<int>& prices) { if(prices.empty()) return 0; int maxProfit = 0; int buy = prices[0]; for(int i = 1; i<prices.size(); i++){ if(prices[i] < buy){ buy = prices[i]; continue; } //maxProfit = max(maxProfit, prices[i] - buy); if(maxProfit < prices[i]-buy) maxProfit = prices[i]-buy; } return maxProfit; } };
第二题:
第二题与第一题的不同在于,不限制交易次数,即可以无限次的交易。
思路:
如果当前价格大于上次的卖出价格,将本次交易的利润(当前价格 - 上次卖出价格)累加到总利润。如果当前价格小于上次卖出价格,那之前的交易“就暂告一段落”,开始新一轮交易,即将买入和卖出价格更新为当前价格。(另外一种更为直观的思路,因为不限制交易次数,所以只需比较相邻两天的价格,有利润累加起来就是最终答案)
代码:
class Solution { public: int maxProfit(vector<int>& prices) { if(prices.empty()) return 0; int profits = 0; int buy = prices[0]; int sell = prices[0]; for(int price : prices){ if(price > sell){ profits += (price - sell); sell = price; } if(price < sell){ buy = sell = price; } } return profits; } };
第三题:
该题将交易次数限制在2次以内,即最多只能交易2次。
思路:
出自http://blog.csdn.net/fightforyourdream/article/details/14503469,这里讲的非常明白,感觉我讲不了这么清晰。
O(n^2)的算法很容易想到:
找寻一个点j,将原来的price[0..n-1]分割为price[0..j]和price[j..n-1],分别求两段的最大profit。
进行优化:
对于点j+1,求price[0..j+1]的最大profit时,很多工作是重复的,在求price[0..j]的最大profit中已经做过了。
类似于Best Time to Buy and Sell Stock,可以在O(1)的时间从price[0..j]推出price[0..j+1]的最大profit。
但是如何从price[j..n-1]推出price[j+1..n-1]?反过来思考,我们可以用O(1)的时间由price[j+1..n-1]推出price[j..n-1]。
最终算法:
数组l[i]记录了price[0..i]的最大profit,
数组r[i]记录了price[i..n]的最大profit。
已知l[i],求l[i+1]是简单的,同样已知r[i],求r[i-1]也很容易。
最后,我们再用O(n)的时间找出最大的l[i]+r[i],即为题目所求。
代码(写的比较蹩脚...):
class Solution { public: int maxProfit(vector<int>& prices) { if(prices.size() <= 1) return 0; int s = prices.size(); int profits = 0; int l[s] = {0}; int r[s] = {0}; int buy = prices[0]; for(int i = 1; i<s; i++){ if(prices[i] - buy > profits){ profits = prices[i] - buy; l[i] = profits; }else{ l[i] = l[i-1]; } if(prices[i] < buy) buy = prices[i]; } profits = 0; int sell = prices[s-1]; for(int i = s-2; i >= 0; i--){ if(sell - prices[i] > profits){ profits = sell - prices[i]; r[i] = profits; }else{ r[i] = r[i+1]; } if(prices[i] > sell) sell = prices[i]; } profits = l[0] + r[0]; for(int i = 1; i<s; i++){ profits = max(profits, l[i]+r[i]); } return profits; } };
第四题:
本题在第三题的基础上,将交易次数推广为k次。略繁琐,可以参考这篇文章http://blog.csdn.net/fightforyourdream/article/details/14503469。
第五题:
本题限制在于卖出的第二天不能立即买入,即有一个冷却时间。
思路:
buy[i] = max(buy[i-1], rest[i-1] - prices[i])
sell[i] = max(sell[i-1], buy[i-1] + prices[i], rest[i-1])
rest[i] = max(buy[i-1], sell[i-1], rest[i-1])
其中,buy[0] = -prices[0],sell[0] = rest[0] = 0。buy[i]表示如果第i天买入当前的最大利润,sell[i]表示如果第i天卖出当前的最大利润,rest[i]表示第i天休息当前的最大利润。rest[i]的递推公式很好理解,第i天休息,没有额外利润,即rest[i]等于前一天三种状态的最大值。sell[i]即今天卖出,可能存在收益,所以需要在“前一天卖出”“今天卖出”“前一天休息”中找最大值,(补充:其实,“前一天休息”是多余的,因为前一天休息肯定不会大于前一天卖出的利润)。buy[i]是如果今天买入后的利润,在“前一天买入”“今天买入”中找最大值,这里的买入限制只能在rest后买入,即rest[i-1] - prices[i],就是题目中限制卖出后第二天立即买入。下面给出一个例子:
第一天只可能buy或rest,buy后的利润就是第一天价格的相反数。
初始值 价格 1 3 2 8 4 9 buy -1 sell 0 rest 0 第二天buy的话,没有第一天buy利润大,保持-1;第二天卖出利润为2;第二天休息利润仍为0。
第二天 价格 1 3 2 8 4 9 buy -1 -1 sell 0 2 rest 0 0
第三天 价格 1 3 2 8 4 9 buy -1 -1 -1 sell 0 2 2 rest 0 0 2
按上述的递推公式继续.......
第六天 价格 1 3 2 8 4 9 buy -1 -1 -1 -1 -1 -1 sell 0 2 2 7 7 8 rest 0 0 2 2 7 7
最大利润即为8。
代码(空间复杂度可以继续优化,由递推公式看出,第i天的利润只与前一天相关,即空间复杂度可以降到O(1)):
class Solution { public: int maxProfit(vector<int>& prices) { if(prices.empty()) return 0; int size = prices.size(); vector<int> buy(size, 0); vector<int> sell(size, 0); vector<int> rest(size, 0); buy[0] = -(prices[0]); for(int i = 1; i<size; i++){ buy[i] = max(buy[i-1], rest[i-1]-prices[i]); sell[i] = max(sell[i-1], buy[i-1]+prices[i]); rest[i] = max(max(buy[i-1], sell[i-1]), rest[i-1]); } return sell[size-1]; } };
第六题:
本题与前面题目的不同之处在于每一次交易都需要上交一定的手续费fee。
思路:
buy[i] = max(buy[i-1], sell[i-1] - prices[i])
sell[i] = max(sell[i-1], buy[i-1] + prices[i] - fee)
与第五题类似,不同的在于交易费,即每次卖出都上交交易费(减去fee)。buy[i]需要在“前一天买入”“前一天卖出后买入”中找最优;sell[i]需要在“前一天卖出”“今天卖出”中找最优。
代码(同样,可以将空间复杂度降低为O(1)):
class Solution { public: int maxProfit(vector<int>& prices, int fee) { if(prices.empty()) return 0; int sell = 0; int buy = -(prices[0]); for(int i = 1; i < prices.size(); i++){ int old = sell; sell = max(sell, buy+prices[i]-fee); buy = max(buy, old-prices[i]); } return sell; } };
总结:
这一系列题,完美的运用了动态规划思想,包含了各种各样的情况,自己的理解还不够充分,有理解不准确的地方还请不吝赐教...一起学习。