Leetcode学习计划之动态规划入门day7(1014,121,122)

目录

1014. 最佳观光组合

问题描述

思路与算法

代码实现

121. 买卖股票的最佳时机

问题描述 

思路与算法

代码实现

122. 买卖股票的最佳时机 II

问题描述

 思路与算法

代码实现


 

1014. 最佳观光组合

问题描述

给你一个正整数数组 values,其中 values[i] 表示第 i 个观光景点的评分,并且两个景点 ij 之间的 距离j - i

一对景点(i < j)组成的观光组合的得分为 values[i] + values[j] + i - j ,也就是景点的评分之和 减去 它们两者之间的距离。返回一对观光景点能取得的最高分。

示例 1:

输入:values = [8,1,5,2,6]
输出:11
解释:i = 0, j = 2, values[i] + values[j] + i - j = 8 + 5 + 0 - 2 = 11

示例 2:

输入:values = [1,2]
输出:2

提示:

  • 2 <= values.length <= 5 * 10^4
  • 1 <= values[i] <= 1000

思路与算法

        记gif.latex?f%28i%2Cj%29代表景点(i < j)组成的观光组合的得分,

        考虑gif.latex?f%28j%29表示从0到j的序列满足题设要求的最高分,问题所求的就是gif.latex?%5Cmax%5Climits_%7B1%5Cleq%20j%20%5Cleq%20n-1%7D%7Bf%28j%29%7D。直观地求gif.latex?f%28j%29需要遍历gif.latex?%5Ctext%7Bi%3D0%2C...%2Cj-1%7D,这样的话总的复杂度就是gif.latex?O%28n%5E2%29

        但是,将分数计算公式做一个变形得到:values[i] + i + values[j] - j,对于gif.latex?f%28j%29的计算来说,后半部分是固定的,因此只要找到前半部分的最大值即可得到gif.latex?f%28j%29如下所示:

gif.latex?f%28j%29%20%3D%20%5Cmax%5Climits_%7B0%5Cleq%20i%20%5Cleq%20j-1%7D%20%7B%28v%5Bi%5D&plus;i%29%7D%20&plus;%20v%5Bj%5D&plus;j

        进一步,在对gif.latex?%5Ctextit%7Bj%7D的遍历过程中,gif.latex?%5Cmax%5Climits_%7B0%5Cleq%20i%20%5Cleq%20j-1%7D%20%7B%28v%5Bi%5D&plus;i%29%7D的计算是可以以迭代的方式进行维护和更新的,这样,gif.latex?f%28j%29的计算复杂度就变成gif.latex?O%281%29,总的复杂度变成gif.latex?O%28n%29

 

代码实现

class Solution:
    def maxScoreSightseeingPair(self, values: List[int]) -> int:
        # Initialization for (0,1)
        max_vi_plus_i = values[0] + 0
        # vj_minus_j    = nums[1] - 1
        max_score     = max_vi_plus_i + values[1] - 1
        
        for j in range(2,len(values)):
            max_vi_plus_i = max(max_vi_plus_i,values[j-1]+j-1)
            max_score = max(max_score,max_vi_plus_i + values[j] - j)

        return max_score

        执行用时:188 ms, 在所有 Python3 提交中击败了13.63%的用户

        内存消耗:19.7 MB, 在所有 Python3 提交中击败了77.22%的用户

 

121. 买卖股票的最佳时机

问题描述 

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:

  • 1 <= prices.length <= 10^5
  • 0 <= prices[i] <= 10^4

思路与算法

        买入必须发生卖出之前。

        考虑gif.latex?f%28j%29代表在day#j卖出该股票所能获得的最大利益。那最终结果就等于gif.latex?%5Cmax%5Climits_%7B1%20%5Cleq%20j%20%5Cleq%20n-1%7D%7Bf%28j%29%7D

        要确保在day#j卖出时的利益最大化,必要day#j之前的最低价时买入该股票。这样可以得到:

gif.latex?f%28j%29%20%3D%20price%5Bj%5D%20-%20%5Cmin%5Climits_%7B0%20%5Cleq%20i%20%3C%20j%7D%7Bprice%5Bi%5D%7D

         而针对每个j的买入最低价是可以以迭代的方式处理,只有gif.latex?O%281%29,因此总的复杂度为gif.latex?O%28n%29

代码实现

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        minprice = prices[0]
        maxprofit= -prices[0]
        for j in range(1,len(prices)):
            profit = prices[j] - minprice
            minprice = min(minprice,prices[j])
            maxprofit= max(maxprofit,profit)
            
        return max(maxprofit,0)

        执行用时:280 ms, 在所有 Python3 提交中击败了42.33%的用户

        内存消耗:23.6 MB, 在所有 Python3 提交中击败了15.69%的用户

 

122. 买卖股票的最佳时机 II

问题描述

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。返回 你能获得的 最大 利润 。

示例 1:

输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
     总利润为 4 + 3 = 7 。

示例 2:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     总利润为 4 。

示例 3:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。

提示:

  • 1 <= prices.length <= 3 * 10^4
  • 0 <= prices[i] <= 10^4

 思路与算法

        在任何一天,只要该天的股价为局部极大点那么都应该在之前最近的一个极小点买入然后在当天卖出。问题描述中“你也可以先购买,然后在 同一天 出售。”,这句话与什么用意,感觉难道不是一句废话吗?

        所以,基本操作规则如下:从左到右遍历,碰到极小点就买入,碰到极大点就卖出。

        但是中间出现平台区会使得判断比较麻烦。以下以一个有限状态机的方式来求解本题(虽然是动态规划系列,但是感觉用有限状态机的方式更易懂,而且偶尔换点口味也不错)

1a6dbf4eb0f748da9c10cd802d9aac9a.png

 

代码实现

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        state, bought, profit  = 0,0,0

        prices.append(0) # Appending one zero for the convenience of last day handling
        for i in range(len(prices)-1):
            if state == 0:
                if prices[i+1]>prices[i]:
                    bought = prices[i]
                    state  = 1
                elif prices[i+1]<prices[i]:
                    state  = -1
            elif state == -1:
                if prices[i+1]>prices[i]:
                    bought = prices[i]
                    state  = 1
            else:
                if prices[i+1]<prices[i]:
                    state  = -1
                    profit += prices[i]-bought
            # print(i,state,bought, profit)

        return profit

if __name__ == "__main__":
    
    sln = Solution()  
    
    prices = [7,1,5,3,6,4]
    print(sln.maxProfit(prices))
    
    prices = [7,6,4,3,1]
    print(sln.maxProfit(prices))
    
    prices = [7,6,4,3,6]
    print(sln.maxProfit(prices))    
    
    prices = [1,2,3,4,5]
    print(sln.maxProfit(prices))  

        执行用时:36 ms, 在所有 Python3 提交中击败了88.26%的用户

        内存消耗:16.1 MB, 在所有 Python3 提交中击败了21.77%的用户

 

        回到总目录:笨牛慢耕的Leetcode每日一题总目录(动态更新。。。)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨牛慢耕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值