leetcode Best Time to Buy and Sell Stock 121 122 309 714 123 188

股票买卖系列问题

说了这么多年的“状态转移方程”这个词,今天似乎有了新的理解。。。动态规划问题可以有等价的状态机。

以前更多的情况是在一个数组中根据相应的转移条件计算当前的值,这一系列问题,需要维护多个数组,正好可以很方便的画出状态机的图。

从易到难记录一下leetcode中这个系列的题。

 121 Best Time to Buy and Sell Stock 

           题目 

 只能进行至多一次买卖,问最多利润。

思路:一个min变量保存当前最小的价格,一个max变量保存当前最大的利润,遍历一遍,更新max和min得到最终结果。

int maxProfit(int* prices, int pricesSize) {
    int max = 0;
    int min = prices[0];
    for(int i = 1; i < pricesSize; i++) 
    {
        if (prices[i] - min > max) max = prices[i] - min;
        if (prices[i] < min) min = prices[i];
    }
    return max;
}

122. Best Time to Buy and Sell Stock II

          题目

可以进行无数次买卖

思路:将所有后一个减前一个的差为正的都加起来。

int maxProfit(int* prices, int pricesSize) {
    int sum = 0,t;
    for (int i = 1; i < pricesSize; i++)
    {
        t = prices[i] - prices[i-1] ;
        if (t > 0)
            sum += t;
    }    
    return sum;
}

309. Best Time to Buy and Sell Stock with Cooldown

          题目

可以进行无数次买卖,但是每次卖出后需要休息一天才可以再次买入。

思路:

可以按照题意画出状态转移图

《leetcode Best Time to Buy and Sell Stock 121 122 309 714 123 188》

分别使用三个数组表示这三个节点的状态,则

    hold[i] = max(wait2[i-1] – prices[i], hold[i-1])

    wait1[i] = hold[i-1] + prices[i]

    wait2[i] = max(wait1[i-1], wait2[i-1])

每次计算出的都是当前天,如果股票交易处于这几个节点时,可以取到的最大值。

每一个节点的值仅与上一次这些节点的值有关,所以可以简化空间复杂度,仅保留上一次的节点值即可。

初始化:初始化时,弄错了很多次,i(也就是天的计数)应该从1开始,而不是0,。hold_old初始化应该为-prices[0],因为第一次出现hold状态是一定是前一次购买,不会是从hold状态到达的hold状态。wait1_old应初始化为负无穷,因为一开始不会出现这个状态,再次更新时一定是要更新的,无论正负都是要得到这个值,开始我初始化为0,出现了负数无法更新的状况。

int maxProfit(int* prices, int pricesSize) {
    int hold = 0, hold_old = -prices[0], wait1 = 0, wait1_old = -1000000,wait2 = 0, wait2_old = 0;
    for (int i = 1; i < pricesSize; i++)
    {
        hold = wait2_old - prices[i] > hold_old ? wait2_old - prices[i] : hold_old;
        wait1 = hold_old + prices[i]; 
        wait2 = wait1_old > wait2_old ? wait1_old : wait2_old;
        hold_old = hold;
        wait1_old = wait1;
        wait2_old = wait2;
    }
    return wait1 > wait2 ? wait1 : wait2;
}

714. Best Time to Buy and Sell Stock with Transaction Fee

          题目

可以无数次买卖,但是每次卖出都需要缴纳手续费(一定为正)。

思路:

参考上一道题,去掉休息一天的条件,修改状态转移图为两个节点,由hold到wait时的利润减少fee。

int maxProfit(int* prices, int pricesSize, int fee) {
    int hold = 0, hold_old = -prices[0], wait = 0, wait_old = 0;
    for (int i = 1; i < pricesSize; i++)
    {
        hold = wait_old - prices[i] > hold_old ? wait_old - prices[i] : hold_old;
        wait = hold_old + prices[i] - fee > wait_old ? hold_old + prices[i] - fee : wait_old;
        hold_old = hold;
        wait_old = wait;
    }
    return wait;
}

123. Best Time to Buy and Sell Stock III

          题目

最多可以买卖2次,求最大利润。

思路:

依旧画出动态转移图

《leetcode Best Time to Buy and Sell Stock 121 122 309 714 123 188》

有了之前的方法状态转移方程就很好写出来了
        s1 = max (s1,-prices[i])

        s2 = max (s2, s1+prices[i])

        s3 = max (s3, s2-prices[i])

        s4 = max (s4,s3+prices[i]);

由于这个状态只与前一个节点有关,更新时只需要从后往前更新便可以不用像前两道题一样保存旧值。

初始化要注意,将s2和s4保存成0,否则只买卖一次会出错。

int maxProfit(int* prices, int pricesSize) {
    int s1=INT_MIN,s2=0,s3=INT_MIN,s4=0;
	for(int i=0;i<pricesSize;++i) 
        {            
	    s4 = s4>s3+prices[i]?s4:s3+prices[i];
	    s3 = s3>s2-prices[i]?s3:s2-prices[i];
            s2 = s2>s1+prices[i]?s2:s1+prices[i];
	    s1 = s1>-prices[i]?s1:-prices[i];
	}
	return s4;
}

 188. Best Time to Buy and Sell Stock IV

          题目

限制最多买卖k次,求最大的利润。

思路:

首先,当k大于天数的一半的时候,我们可以将它等同于无数次买卖求最大利润的情况。

然后,当k小于天数一半的时候,我们继续沿用上一道题的思路,构造一个2k个节点单向的状态机。可以看出,状态是重复的买和卖,也就是s3,s4和s1,s2状态转移方式相同,可以使用for循环进行更新。注意:逆序。

int maxProfit(int k, int* prices, int pricesSize) {
    if (k == 0) return  0;
    int s1[10000], s2[10000];
   
    if (k > pricesSize / 2)
    {
        int sum = 0;
        for (int i = 1; i < pricesSize; i++)
        {
            if (prices[i] > prices[i - 1]) sum += prices[i] - prices[i - 1];
        }
        return sum;
    }
    for (int i = 0; i <= k; i++)
    {
         s1[i] = INT_MIN;
         s2[i] = 0;
    }
	for (int i = 0; i < pricesSize; i++)
    {            
        for (int j = k; j > 0; j--)
        {
            s2[j] = s2[j] > s1[j] + prices[i] ? s2[j] : s1[j] + prices[i];
	        s1[j] = s1[j] > s2[j-1] - prices[i] ? s1[j] : s2[j - 1] - prices[i];
        }
	}
	return s2[k];

}

 

点赞