Day27 貪心演算法part1

haohaoscnblogs發表於2024-08-12

任務

455. 分發餅乾

假設你是一位很棒的家長,想要給你的孩子們一些小餅乾。但是,每個孩子最多隻能給一塊餅乾。
對每個孩子 i,都有一個胃口值 g[i],這是能讓孩子們滿足胃口的餅乾的最小尺寸;並且每塊餅乾 j,都有一個尺寸 s[j] 。如果 s[j] >= g[i],我們可以將這個餅乾 j 分配給孩子 i ,這個孩子會得到滿足。你的目標是儘可能滿足越多數量的孩子,並輸出這個最大數值。

思路

對s和g排序,每次所能滿足的最大胃口的孩子,計數。注意要從胃口開始倒序遍歷,索引自然遞減,而對於尺寸的遞減,則透過index的形式,每用掉一個,遞減,並且統計分配數量。
注意自然遞減不能用尺寸,模擬一下可以發現

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        s.sort()
        g.sort()
        index = len(s)-1
        count = 0
        for i in range(len(g)-1,-1,-1): #這裡必須遍歷g而非s
            if index >=0 and g[i] <= s[index]:
                count += 1
                index -= 1
        return count

376. 擺動序列

如果連續數字之間的差嚴格地在正數和負數之間交替,則數字序列稱為 擺動序列 。第一個差(如果存在的話)可能是正數或負數。僅有一個元素或者含兩個不等元素的序列也視作擺動序列。

例如, [1, 7, 4, 9, 2, 5] 是一個 擺動序列 ,因為差值 (6, -3, 5, -7, 3) 是正負交替出現的。

相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是擺動序列,第一個序列是因為它的前兩個差值都是正數,第二個序列是因為它的最後一個差值為零。
子序列 可以透過從原始序列中刪除一些(也可以不刪除)元素來獲得,剩下的元素保持其原始順序。

給你一個整數陣列 nums ,返回 nums 中作為 擺動序列 的 最長子序列的長度 。

思路

本題很難想,最基礎的思路是,用prevDiff和nextDiff記錄當前的節點的前後diff,如果異號,則說明可以保留並且統計加1,即基礎條件為if preDiff >0 and nextDiff < 0 or preDiff<0 and nextDiff > 0,然後遍歷過程中prevDiff隨著nextDiff修改

  • 情況一: 首尾節點的處理,首節點,假裝之前還有一個和他相等的節點,那麼是否統計第一個節點只看和第二個節點的關係即nextDiff,為了統一邏輯,則上面的基礎條件變為 if preDiff >= 0 and nextDiff < 0 or preDiff<=0 and nextDiff > 0
  • 情況二: 非單調有平坡,可以延續情況一的邏輯
  • 情況三: 單調有平坡,將修改時機從 遍歷過程中prevDiff隨著nextDiff修改 改為,只有發生方向變化時,才修改prevDiff,這個很難想到
    從看完題解再分析,可以分析思路是找到需要保留的節點,因為最終形成的是擺動序列,所以最終形成的prev,diff是隻記錄方向變化的
class Solution376:
    def wiggleMaxLength(self, nums: List[int]) -> int: 
        if len(nums) == 1: 
            return 1
        
        preDiff = 0
        nextDiff = 0
        result = 1 # 預設最後一個為一個波動
        
        for i in range(len(nums)-1):
            nextDiff = nums[i+1] - nums[i]
            if preDiff >= 0 and nextDiff < 0 or preDiff<=0 and nextDiff > 0:
                result +=1    
                preDiff = nextDiff # 只有方向改變時才修改prediff、
        return result

53. 最大子陣列和

給你一個整數陣列 nums ,請你找出一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。
子陣列是陣列中的一個連續部分。

思路

暴力的解法是找到每一個索引為首的最大和連續子陣列,複雜度為O(n^2)。
考慮動規的解法,dp[i]表示以當前索引i為結尾的最大子陣列和,那麼dp[i-1]表示以索引i-1為結尾的最大子陣列和,dp[i]就取包含以i-1結尾的子序列和不包含以i-1結尾的子序列兩種情況中的最大值,即dp[i] = max(dp[i-1]+nums[i],nums[i])

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        dp = [0] * len(nums) # dp陣列的元素: 以當前索引i結尾的最大和連續子陣列的長度
        dp[0] = nums[0]
        
        for i in range(1,len(nums)):
            dp[i] = max(dp[i-1]+nums[i],nums[i])
        
        return max(dp)

貪心思路:當遍歷到某個數和為負數時,則以下一個數為起點,重新累加

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        result = float("-inf")
        sum = 0 
        for i in range(len(nums)):
            sum += nums[i]
            result = sum if result<sum else result
            if sum < 0:
                sum = 0
        return result

心得體會

貪心法的思路非常靈活,且是用區域性最優來嘗試是否達到全域性最優,其中的證明也是比較困難的,現階段還無法嚴格證明,只能去模擬看是否可以透過,或無法舉出反例的情況,可以鍛鍊一定的思維能力,想不出來可以看題解,看懂思路後編碼即可,不必追求完美。本章的目標就是理解思路後的編碼能力.

相關文章