【leetcode系列】小菜鸡的leetcode第19题:动态规划入门(6)最优观光组合/买卖股票的最优时机/买卖股票的最优时机II/最优买卖股票时机含冷冻期/买卖股票的最优时机含手续费

题目分别如下:

三道题如下,其实仔细观察,不难发现,第一题和第二题思路几乎一模一样,就是换了个说法,换汤不换药

主要思路为:

【指针i固定,其对应的values[i]也是固定的,也就是说,只需要确定某个数值的指针位置,其对应的数值确定。观光问题比股票问题稍微复杂一点点,i<j:max(values[i]+values[j]+i-j),可以写为i<j:max(values[i]+i)+max(values[j]-j),看成确定的两数之和,分别更新确定两数最大值即可。需要注意的一点是,条件为:i<j,即先更新j对应的values[j]-j,再更新values[i]+i】

#观光
class Solution:
    def maxScoreSightseeingPair(self, values: List[int]) -> int:
        res = 0
        premax = values[0] + 0 #初始值
        for j in range(1, len(values)):
            res = max(res, premax + values[j] - j) #判断能否刷新res
            premax = max(premax, values[j] + j) #判断能否刷新pre_max, 得到更大的A[i] + i        
        return res
#股票
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n=len(prices)
        ans=prices[0]
        maxcount=0
        for i in range(n):
            maxcount=max(maxcount,prices[i]-ans)
            ans=min(ans,prices[i])
            #print(ans,maxcount)
        return maxcount

股票II继续延续上述思路,但多了一点点更有趣的思路,即贪心算法求局部最优解 。图解版晚点更新:

#类似于之前的“跳跃游戏”,在每个局部中找到局部最优解,局部的最大最小值,每次检索到局部最大值时停下,必为某局部的最大利润,然后继续进行下一次重复搜索,最后进行局部最大值的加和,即为整体最大值

#股票进阶II
class Solution:
    #法1:效率最高,速度最快
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        summax = 0
        for i in range(1, n):
            p = prices[i - 1]
            maxp = 0
            # p=min放第一步表示当天可以买卖,利润为0;放后面则表示当天不可买卖,只能减去前n天的价格
            # 类似于“跳跃游戏?”,在每个局部中找到局部最优解,局部的最大最小值,每次检索到局部最大值时停下,必为某局部的最大利润,然后继续进行下一次重复搜索,最后进行局部最大值的加和,即为整体最大值

            # 当某点达到局部最大值时:
            if p < prices[i]:
                # if prices[i-1]<prices[i]:
                # p=prices[i-1]
                # maxp=-inf
                # 更新局部区域内的最小价格
                p = min(p, prices[i])
                # p=min(prices[i-1],prices[i])
                # 更新局部区域内的最大价格差
                maxp = max(maxp, prices[i] - p)
                # oldp=prices[i]
                # print(p,prices[i],maxp)
                # if oldp>=prices[i]:
                summax += maxp
                # print(p, maxp, summax)
            # print(summax)
            # oldp=prices[i]
            # print(oldp)
        return summax
    
    #法2:
    def maxProfit(self, prices) -> int:
        n = len(prices)
        summax = 0
        p = prices[0]
        maxp = 0
        for i in range(1, n):
            # 类似于“跳跃游戏?”,在每个局部中找到局部最优解,局部的最大最小值,每次检索到局部最大值时停下,必为某局部的最大利润,然后继续进行下一次重复搜索,最后进行局部最大值的加和,即为整体最大值
            # 当某点在局部最大值之前时(局部最大值时为prices[i-1]>=prices[i]):
            if prices[i - 1] < prices[i]:
                # 更新局部区域内的最小价格
                # p=min放第一步表示当天可以买卖,利润为0;放后面则表示当天不可买卖,只能减去前n天的价格
                p = min(p, prices[i])
                # p=min(prices[i-1],prices[i])
                # 更新局部区域内的最大价格差
                maxp = max(maxp, prices[i] - p)
            # else:
            # 当达到局部最大值时prices[i-1]>=prices[i]时,重新开始下一局部区域的检索,并把前一局部最优解保存:
            summax += maxp
            # print(summax)
            p = prices[i]
            # print(summax,p)
            maxp = 0
            # print(summax)
            # oldp=prices[i]
            # print(oldp)
        return summax
    【#法2清晰版思路:】
     def maxProfit(self, prices: List[int]) -> int:
        #增加一个元素-inf,避免最后一组局部最优一直为递增,无法跳出循环更新summax
        prices.append(-inf)
        n = len(prices)
        summax = 0
        p = prices[0]
        maxp = 0
        for i in range(1, n): 
            #递增时不断更新最小值和最大值,直到局部检索到最大值-最小值时候跳出循环
            if prices[i - 1] < prices[i]:          
                #p = min(p, prices[i])
                maxp = max(maxp, prices[i] - p)
                #递增时累加即可,后面肯定大于前面,8-2=8-6+6-2
                #maxp+=prices[i]-prices[i-1]   
                #print(maxp) 
            #跳出循环后(即下一个局部最优解的开始小于前一个值),进行最大差值的累加
            else:
                summax += maxp
                p = prices[i]
                #print(summax)
                maxp = 0
        #summax=max(summax,maxp)
        return summax
    【法2清晰思路优化进阶版,代码极简,类似于法3贪心算法,但通过if判断大大提高了法3的运行效率】
     def maxProfit(self, prices: List[int]) -> int:
        #增加一个元素-inf,避免最后一组局部最优一直为递增,无法跳出循环更新summax
        prices.append(-inf)
        n = len(prices)
        #summax = 0
        #p = prices[0]
        maxp = 0
        for i in range(1, n): 
            #递增时不断更新最小值和最大值,直到局部检索到最大值-最小值时候跳出循环
            if prices[i - 1] < prices[i]:          
                #maxp = max(maxp, prices[i] - p)
                #递增时累加即可,后面肯定大于前面,8-2=8-6+6-2
                maxp+=prices[i]-prices[i-1]   
        return maxp
    #法3:思路最简单,但执行效率并非最高,贪心算法
    def maxProfit(self, prices: List[int]) -> int:
        n=len(prices)
        for i in range(1,n):
            #maxp=max(maxp,maxp+prices[i]-prices[i-1])
            maxp+=max(0,prices[i]-prices[i-1])
        return maxp
【法1-3都为贪心算法思路,法4为状态方程】
    #法4
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        #两个维度,第一维储存时间信息,第二维储存状态信息(持有或卖出)
        #dp=[0]*(n,2)
        #dp=[[0 for col in range(2)]for row in range(n)]
        #dp[0][1]=-prices[0]
        own=-prices[0]
        #dp[0][0]=0
        noown=0
        for i in range(1,n):
            #dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i])
            own=max(own,noown-prices[i])
            #dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i])
            noown=max(noown,own+prices[i])
        #return dp[n-1][0]
        return noown
   

法2清晰思路执行效率,每次只更新最大差值:

【法2清晰思路优化进阶版,代码极简,类似于法3贪心算法,但通过if判断大大提高了法3的运行效率】

法3数学解法: 

zhengitla

股票II问题整体来说有两种思路:

(1)贪心算法,从前往后依次求解局部最优解

(2)寻找状态方程的通解,然后递归【这里引入了二维动态规划矩阵,原因是存在两个维度的变量:时间维度(指针)和当前状态维度(0或1,即未持有(卖出状态)或者持有(买入状态)),以后可以进一步进行状态变量扩展】

最佳买卖股票时机含冷冻期

力扣https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n==1:
            return 0
        #两个维度,第一维储存时间信息,第二维储存状态信息(持有或卖出)
        #dp=[0]*(n,2)
        dp=[[0 for col in range(2)]for row in range(n)]
        dp[0][1]=-prices[0]
        #own=-prices[0]
        dp[0][0]=0
        dp[1][1]=max(dp[0][1],-prices[1])
        dp[1][0]=max(dp[0][0],dp[0][1]+prices[1])
        #noown=0
        for i in range(2,n):
            #持有[i][1] 
            #持有状态仅跟前一状态和前两状态有关,即买入时间的限制
            dp[i][1]=max(dp[i-1][1],dp[i-2][0]-prices[i])
            #dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i])
            #own=max(own,noown-prices[i])
            #不持有[i][0],什么时候都可以卖出,无限制
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i])
            #noown=max(noown,own+prices[i])
        return dp[n-1][0]
        #return noown

 #优化内存,动态规划
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n==1:
            return 0
        #两个维度,第一维储存时间信息,第二维储存状态信息(持有或卖出)
        #dp=[0]*(n,2)
        #dp=[[0 for col in range(2)]for row in range(n)]
        #dp[0][1]=-prices[0]
        #own=-prices[0]
        #dp[0][0]=0
        #dp[1][0]=max(dp[0][0],dp[0][1]+prices[1])
        #dp[1][1]=max(dp[0][1],-prices[1])
        a1=0
        #dp[1][0]
        a2=max(0,-prices[0]+prices[1])
        #dp[1][1]
        b1=max(-prices[0],-prices[1])
        #noown=0
        for i in range(2,n):
            b2=b1
            #持有[i][1] 
            #持有状态仅跟前一状态和前两状态有关,即买入时间的限制
            #dp[i][1]=max(dp[i-1][1],dp[i-2][0]-prices[i])
            b1=max(b1,a1-prices[i])
            #own=max(own,noown-prices[i])
            #不持有[i][0],什么时候都可以卖出,无限制
            #dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i])
            a1=a2
            a2=max(a2,b2+prices[i])
            #noown=max(noown,own+prices[i])
        #return dp[n-1][0]
        return a2
        #return noown

优化后的执行效率:

买卖股票的最佳时机含手续费

力扣https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/

#法1:状态方程/动态规划求解
class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        n=len(prices)
        #dp=[[0 for i in range(2)] for j in range(n)]
        #i=1
        #dp[0][1]=-prices[0]
        #dp[0][0]=0
        a1=-prices[0]
        b1=0
        #print(dp)
        for i in range(1,n):
            a2=a1
            #持有
            #dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i])
            a1=max(a1,b1-prices[i])
            #未持有
            #dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]-fee)
            b1=max(b1,a2+prices[i]-fee)
        return b1
#法2:贪心算法(代码比较妙,尤其是第二个if后的"buy=prices[i]"可以说是避免了重复循环和多变量的点睛之笔)
    def maxProfit(self, prices: List[int], fee: int) -> int:
        #贪心算法,以卖出价来看(卖出后进行操作,买入先进行操作,因此以最后一个操作为准,依次进行局部最优求解)
        #以当前拥有的profit来看:
        #卖出:+pricesp[j]
        #买入:-prices[i]
        #手续费:-fee
        #因此,每进行一笔交易,买入花费总价-prices[i]-fee
        n=len(prices)
        buy=prices[0]+fee
        maxp=0
        for i in range(1,n):
            #如果当前购入价格小于初始购入价格,则更新新的购入价格(买更便宜的)
            if prices[i]+fee<buy:
                buy=prices[i]+fee
                print(buy)
            #如果卖出价大于之前最小买入价,那么进行交易,一定可以获得利润
            #
            if prices[i]>buy:
                maxp+=prices[i]-buy 
                buy=prices[i]
                #print(maxp)
        return maxp

法1状态方程: 

法2贪心算法: 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值