leetcode笔记--Best Time to Buy and Sell Stock系列(I,II, III, IV)

Best Time to Buy and Sell Stock

题目:难度(Easy)

Say you have an array for which the ith element is the price of a given stock on day i.
If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.
Tags:Array,Dynamic Programming

分析:

        交易次数至多为1次。设dp[i]表示第i天卖出能获得的最大利润,那么就需要在第i天之前买进,而且为使得利润最大,应在第i天之前的prices里最小的那一天买进,记那一天的price为buyPrice,则max(dp)即为所求。由于买进的那一天已经由buyPrice记录下来了,而dp[i]只与buyPrice相关,则可使用变量dp逐步迭代,将空间复杂度降为O(1)  。该算法的时间复杂度是O(n)

代码实现:

import sys
class Solution(object):       
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        n = len(prices)
        #dp = [0]*n
        #初始化
        buyPrice = sys.maxint
        dp = 0#dp表示在第i天卖出能获得的最大利润
        for i in range(1, n):
            buyPrice = min(buyPrice, prices[i-1])
            dp = max(dp, prices[i]-buyPrice)
        return dp


Best Time to Buy and Sell Stock II

题目:难度(Medium)

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 as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
Tags:Array,Greedy

分析:

    在Best Time to Buy and Sell Stock I的基础上将交易的次数扩展为不限次数。贪心策略:在他要贬值之前卖掉它,在增值之前买进。即如果持续增值,就增到最大时卖掉(下一天开始贬值);如果持续贬值,就贬到最小值时买进(下一天会开始增值)。想象股票价格是一个随时间(天数)变化的折线图,那么我们总是关注“递增区间”,且在递增折线的起点买进,在递增折线的终点卖出,然后虐、略过“递减区间”,寻找下一个“递增区间”,在下一个递增折线的起点再次买进,在终点再次卖出。事实上当脑海里有了这幅图的画面,我们就不难发现整个计算过程可以简化为:只要当天的价格高于前一天的价格,就算入收益。空间复杂度降为O(1) ,时间复杂度是O(n)

代码实现:

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        只要当天的价格高于前一天的价格,就算入收益
        """
        n = len(prices)

        maxProfit = 0
        for i in range(1, n):
            if prices[i] > prices[i-1]:
                maxProfit += prices[i]-prices[i-1]
            
        return maxProfit


Best Time to Buy and Sell Stock III

题目:难度(Hard)

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.
Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
Tags:Array Dynamic Programming

分析:

        在Best Time to Buy and Sell Stock I 的基础之上将交易次数扩展为至多2次。那么以第i天为分界线,计算第i天之前(包含第i天)至多进行一次交易的最大收益preProfit[i],和第i天之后(包含第i天)至多进行一次交易的最大收益postProfit[i]。最后再遍历一遍,max{preProfit[i] + postProfit[i]} (0≤i≤n-1)就是最大收益。由于即使在第i天既进行了买交易又进行了卖交易,这2笔交易的收益为0,即不影响计算最终受益结果的计算,故preProfit[i]与postProfit[i]在计算时均包含第i天。第i天之前和第i天之后进行一次的最大收益求法同Best Time to Buy and Sell Stock I。时间复杂度为O(n)  。

代码实现:

import sys
class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """      
        n = len(prices)
        preProfit = [0]*n
        postProfit = [0]*n

        minPrice = sys.maxint
	#minPrice记录在第i天之前(含第i天)prices最小的那一天买进
        for i in range(1, n):
            minPrice = min(minPrice, prices[i-1])
            preProfit[i] = max(preProfit[i-1], prices[i]-minPrice)
           
        maxPrice = -sys.maxint-1
	#maxPrice记录在第i天之后(含第i天)prices最大的那一天卖出
        for i in range(n-2, -1, -1):
            maxPrice = max(maxPrice, prices[i+1])
            postProfit[i] = max(postProfit[i+1], maxPrice-prices[i])
            
        maxProfit = 0
        for i in range(n):
            maxProfit = max(maxProfit, preProfit[i] + postProfit[i])
        return maxProfit

Best Time to Buy and Sell Stock IV

题目:难度(Hard)

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 k transactions.
Notes:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
Tags:Dynamic Programming

分析:

        问题的实质是从长度为n的prices数组中挑选出至多2 * k个元素,组成一个交易(买卖)序列。交易序列中的首次交易为买进,其后卖出和买进操作交替进行。总收益为交易序列中的偶数项之和 - 奇数项之和。
        在Best Time to Buy and Sell Stock I 的基础之上将交易次数扩展为至多k次。设dp[j]表示在第i天之前(含第i天)完成j次“原子交易”时的最大收益(原子交易指一次买进或者一次卖出),则转移方程如下:dp[j] = max(dp[j], dp[j - 1] + prices[i] * [1, -1][j % 2])。当j为奇数时,交易类型为买进(因为第1笔交易只能是买进);当j为偶数时,交易类型为卖出。

        注意:当k过于大时,由上面的递归式写出的程序会有Runtime Error: MemoryError,故可对程序作出这样的优化:当k>n/2时,即转化为交易次数不再受限制,即此题就转化为Best Time to Buy and Sell Stock II的计算。

        空间复杂度降为O(k)  ,时间复杂度是O(n*k)

代码实现:

class Solution(object):
    def maxProfit(self, k, prices):
        """
        :type k: int
        :type prices: List[int]
        :rtype: int
        """
        n = len(prices)
        if k >= n/2:
            return self.quickSolve(prices)
            
        #dp = [0]*(2*k+1)
        dp = [None]*(2*k+1)
        """
        注意:这里要尤其注意一下dp的初始化,用0初始化结果就会不对,为什么呢?
        因为后面的计算中dp[j] = max(dp[j], dp[j-1] + prices[i] * [1, -1][j % 2]),如果dp[j-1] + prices[i] * [1, -1][j % 2]是个负数,那么dp[j] = max(None, 负数) = 负数,但如果初始化为0,这种情况下就会返回0。那么为什么会产生最大利润是负数且还要保留下来供后面计算使用呢?因为在j次原子交易中,中间的任何一次买进操作都有可能造成当前利润是负数,而他们又影响到后面交易的计算,所以要保留。
        """
        
        dp[0] = 0#第0次交易记为0,实际上第一次交易的“买进操作”才是真正的第一有效操作

        for i in range(n):
            #注意:i的有效计数从0开始而j的有效计数从1开始,所以这里j的范围是到min(2*k, i+1)而不是min(2*k, i)
            for j in range(1, min(2*k, i+1)+1):
                dp[j] = max(dp[j], dp[j-1] + prices[i] * [1, -1][j % 2])
        return dp[2*k]
        
    def quickSolve(self, prices):
        n = len(prices)
        maxProfit = 0
        for i in range(1, n):
            if prices[i] > prices[i-1]:
                maxProfit += prices[i] - prices[i-1]
        return maxProfit

参考:

        http://bookshadow.com/weblog/2015/02/18/leetcode-best-time-to-buy-and-sell-stock-iv/

        http://blog.csdn.net/ljiabin/article/details/44900389



你可能感兴趣的:(dynamic,programming,leetcode,greedy)