"""
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)