LeetCode | 2023.02.21 | 1326. 灌溉花園的最少水龍頭數目

"""
https://leetcode.cn/problems/minimum-number-of-taps-to-open-to-water-a-garden/solution/cpython3java-1dp2ceng-forxun-huan-2tan-x-evad/
題目分析:
    這個是經典的一題雙解,可以用dp和貪心
理論1: dp,注意range中slot本身可以澆當前點
    1.定義landRange,用以存儲每個range的left,right
             -2 -1  0  1  2  3
            left       3        right
     left=max(0,slot-rng)               不過<0
     right=min(n,slot+rng)              不會>n
    2.將每個點的left,right記入landRange
    3.根據左邊界排序
    4.定義dp,並存入一個inf表示極大值
    5.循環每個點
    6.循環左右邊界,並計算dp轉換方程
        dp[j]=min(dp[j],dp[start]+1)
    7.每次中間插入判斷left==inf,就返回-1
理論2: 貪心
    思路和dp其本一致,就是用兩個指針prev和last不停交換進行處理
    pass
注意/難點:
    
"""
class Solution:
    def minTaps_dp(self, n, ranges):
        landRange=[]                #定義landRange,用以存儲每個range的left,right
        for slot,rng in enumerate(ranges):
            left=max(0,slot-rng)        #左邊界
            right=min(n,slot+rng)       #右邊界
            landRange.append([left,right])    #左右邊界記入list
        landRange.sort(key=lambda x:x[0])     #根據左邊界排序
        
        inf=10**9                            #定義極大值
        dp=[inf]*(n+1)                       #定義dp,共n+1個水龍頭
        dp[0]=0                              #初始化0值
        for left,right in landRange:
            if dp[left]==inf:                #開始就沒有值,直接返回值
                return -1
            for j in range(left,right+1):    #right+1需要cover right本身
                #debug
                # print(dp[j],dp[left]+1)
                dp[j]=min(dp[j],dp[left]+1)  #轉移方程,不斷更新dp中的值
        return dp[n]
    
    def minTaps_greedy(self, n, ranges):
        ans=0
        furthest=[0]*(n+1)                 #定義列表,用以存儲每個每個最遠值
        #預處理最遠值
        for slot,rng in enumerate(ranges):       #循環水龍頭下標,以及範圍
            left=max(0,slot-rng)          #左邊界
            right=min(n+1,slot+rng)       #右邊界
            furthest[left]=max(furthest[left],right)    #記錄最遠值
        #循環處理結果
        prev,last=0,0
        for i in range(n):
            last=max(last,furthest[i])      #獲取當前slot最遠值
            if last<=i:                     #上次沒有能cover,直接返回值
                return -1
            if prev==i:                     #取當前位置
                prev=last                   #更新prev值
                ans+=1                      #記數+1
        return ans
    
n = 5
ranges = [3,3,2,1,0,1]
# n = 3
# ranges = [0,0,0,0]
# n=7
# ranges=[1,2,1,0,2,1,0,1]

# ans=Solution().minTaps_dp(n,ranges)
ans=Solution().minTaps_greedy(n,ranges)
print(ans)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章