【题目】
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most two transactions.
一个数组price[i]表示第i天股票的价格,你可以操作两次(进行两次买卖),求最大获利。
【解析】
又一道动态规划。
假设刚开始我一毛钱都没有。
第i天可以进行的操作有4种:
1. 前i天只进行一次买进操作。profit_Buy1Sell0[i]
毫无疑问这是亏本的。如果买进第i天的股票,则盈利为-price[i],如果不买第i天的股票,那么去“前i-1天只进行一次买进操作”的过程里去找对应的值profit_Buy1Sell0[i-1],比较-price[i]和profit_Buy1Sell0[i-1]的大小取较大者,即为该操作下的最大盈利(最小亏损),赋值给profit_Buy1Sell0[i]
即,
profit_Buy1Sell0[i] = max ( -price[i] , profit_Buy1Sell0[i-1] )
2. 前i天进行一次买进和一次卖出操作。profit_Buy1Sell1[i]
在第i天的时候,我有两种选择:
第一种,卖出当天的股票,前提是我手上已经有一支股票了,那么我就去“前i-1天只进行一次买进操作”的过程里取找对应的值profit_Buy1Sell0[i-1],接下来将前i-1天买到的那只股票卖掉,那么盈利为—— profit_Buy1Sell0[i-1] + price[i]
第二种,当天不进行操作,也就是说在前i-1天已经进行了一次买进和一次卖出的操作了。则此时盈利为:profit_Buy1Sell1[i-1]
比较以上两种操作方法,取最大值赋给profit_Buy1Sell1[i]
即,
profit_Buy1Sell1[i] = max ( profit_Buy1Sell0[i-1] + price[i] , profit_Buy1Sell1[i-1] )
3. 前i天进行两次买进和一次卖出操作。profit_Buy2Sell1[i]
同样到了第i天,我有两种两种选择:
买进当天的股票,或者不买。
同上,若买进,前i-1天就进行了一次买进和一次卖出,去找profit_Buy1Sell1[i-1],在减掉购买当天股票的成本,即为profit_Buy1Sell1[i-1]-price[i]。
若不买,则前i-1天已经完成了两次买进一次卖出的操作,去找profit_Buy2Sell1[i-1]。
取两者最大值赋给profit_Buy2Sell1[i]
即,
profit_Buy2Sell1[i] = max ( profit_Buy1Sell1[i-1]-price[i] , profit_Buy2Sell1[i-1] )
4. 前i天进行两次买进和两次次卖出操作。profit_Buy2Sell2[i]
到了这一步思路应当逐渐清晰,两种选择:
卖出当天股票,获益为profit_Buy2Sell1[i-1] + price[i]
不卖当天股票,获益为前i-1天进行两买两卖的获益,即profit_Buy2Sell2[i-1]
取两者最大值,即为:
profit_Buy2Sell2[i] = max ( profit_Buy2Sell1[i-1] + price[i] , profit_Buy2Sell2[i-1] )
最后,比较profit_Buy1Sell1[i] 和 profit_Buy2Sell2[i] ,输出两者的较大值。
注:profit_Buy1Sell1[i] 若较大,是否是只进行了一次操作,从而与题意不符???
从讨论的结果看来,此题应该是考虑了当天买当天卖的这种情况,也合情合理,买卖一次就能利益最大化,为什么还要乱操作一通。。。
上述四种操作,编程的时候可以建立4个不同的数组,从0到i遍历price数组,将4个数组逐个填入对应数。
为减少空间复杂度,可以用current[0-3]和next[0-3]来保存当日操作的盈利数据。
【程序】
理解版:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len = prices.size();
int profit_Buy1Sell0[len+1]={INT_MIN};
int profit_Buy1Sell1[len+1]={0};
int profit_Buy2Sell1[len+1]={INT_MIN};
int profit_Buy2Sell2[len+1]={0};
for(int i = 0; i<len; i++)
{
profit_Buy1Sell0[i+1] = max(profit_Buy1Sell0[i], -prices[i]);
profit_Buy1Sell1[i+1] = max(profit_Buy1Sell0[i] + prices[i] , profit_Buy1Sell1[i]);
profit_Buy2Sell1[i+1] = max(profit_Buy1Sell1[i] - prices[i] , profit_Buy2Sell1[i]);
profit_Buy2Sell2[i+1] = max(profit_Buy2Sell1[i] + prices[i] , profit_Buy2Sell2[i]);
}
return max(profit_Buy1Sell1[len],profit_Buy2Sell2[len]);
}
};
优化之后:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int states[2][4] = {INT_MIN, 0, INT_MIN, 0}; // 0: 1 buy, 1: one buy/sell, 2: 2 buys/1 sell, 3, 2 buys/sells
int len = prices.size(), i, cur = 0, next =1;
for(i=0; i<len; ++i)
{
states[next][0] = max(states[cur][0], -prices[i]);
states[next][1] = max(states[cur][1], states[cur][0]+prices[i]);
states[next][2] = max(states[cur][2], states[cur][1]-prices[i]);
states[next][3] = max(states[cur][3], states[cur][2]+prices[i]);
swap(next, cur);
}
return max(states[cur][1], states[cur][3]);
}