@@ -127,23 +127,21 @@ class Solution:
127127
128128![ ] ( https://tva1.sinaimg.cn/large/007S8ZIlly1ghluind4orj30yg0i00um.jpg )
129129
130- 如图:
130+ 上一题的思路是维护一个 furthest,end 变量,不断贪心更新。 这一道题也是如此,不同的点是本题的数据是一个二维数组。 不过如果你彻底理解了上面的题,我想这道题也难不倒你。
131131
132- - 1 不可以,因此存在断层
133- - 2 可以
134- - 3 不行,因为不到 T
132+ 我们来看下这道题究竟和上面的题有多像。
135133
136- 我们当前的 clip 开始结束时间分别为 s,e。 上一段 clip 的结束时间是 t1,上上一段 clip 结束时间是 t2。
134+ 以题目给的数据为例:
137135
138- 那么这种情况下 t1 实际上是不需要的,因为 t2 完全可以覆盖它:
136+ clips = [[ 0,1 ] , [ 6,8 ] , [ 0,2 ] , [ 5,6 ] , [ 0,4 ] , [ 0,3 ] , [ 6,7 ] , [ 1,3 ] , [ 4,7 ] , [ 1,4 ] , [ 2,5 ] , [ 2,6 ] , [ 3,4 ] , [ 4,5 ] , [ 5,7 ] , [ 6,9 ]] , T = 9
139137
140- ![ ] ( https://tva1.sinaimg.cn/large/007S8ZIlly1ghluinuuz2j30o604s3yo.jpg )
138+ 我们对原数组按开始时间排序,并先看前面的一部分:
141139
142- 那什么样 t1 才是需要的呢?如图:
140+ [[ 0,1 ] , [ 0,2 ] , [ 0,3 ] , [ 0,4 ] , [ 1,3 ] , [ 1,4 ] , [ 2,5 ] , [ 2,6 ] ... ]
143141
144- ![ ] ( https://tva1.sinaimg.cn/large/007S8ZIlly1ghluis28zej30mc05saa7.jpg )
142+ > 注意并不需要真正地排序,而是类似桶排序的思路,使用额外的空间,具体参考代码区
145143
146- 用代码来说的话就是 ` s > t2 and t2 <= t1 `
144+ 这是不是就相当于上面跳跃游戏中的: [ 4,0,2 ] 。 至此我们成功将这道题转换为了上面已经做出来的题。 只不过有一点不同,那就是上面的题保证可以跳到最后,而这道题是可能拼不出来的,因此这个临界值需要注意,具体参考后面的代码区。
147145
148146#### 代码
149147
@@ -155,27 +153,32 @@ Python3 Code:
155153
156154class Solution :
157155 def videoStitching (self , clips : List[List[int ]], T : int ) -> int :
158- # t1 表示选取的上一个clip的结束时间
159- # t2 表示选取的上上一个clip的结束时间
160- t2, t1, cnt = - 1 , 0 , 0
161- clips.sort(key = lambda a : a[0 ])
156+ furthest = [0 ] * (T)
157+
162158 for s, e in clips:
163- # s > t1 已经确定不可以了, t1 >= T 已经可以了
164- if s > t1 or t1 >= T:
165- break
166- if s > t2 and t2 <= t1:
167- cnt += 1
168- t2 = t1
169- t1 = max (t1,e)
170- return cnt if t1 >= T else - 1
159+ for i in range (s, e + 1 ):
160+ # 无需考虑,这也是我可以建立一个大小为 T 的 furthest 的数组的原因
161+ if i >= T:break
162+ furthest[i] = max (furthest[i], e)
163+ # 经过上面的预处理,本题和上面的题差距以及很小了
164+ # 这里的 last 相当于上题的 furthest
165+ end = last = ans = 0
166+ for i in range (T):
167+ last = max (last, furthest[i])
168+ # 比上面题目多的一个临界值
169+ if last == i: return - 1
170+ if end == i:
171+ ans += 1
172+ end = last
173+ return ans
171174
172175```
173176
174177** 复杂度分析**
175178
176- - 时间复杂度:由于使用了排序(假设是基于比较的排序),因此时间复杂度为 $O(NlogN)$ 。
179+ - 时间复杂度:$O(\sum _ {i=1}^{n}ranges [ i ] + T)$,其中 ranges [ i ] 为 clips [ i ] 的区间长度 。
177180
178- - 空间复杂度:$O(1 )$。
181+ - 空间复杂度:$O(T )$。
179182
180183### 1326. 灌溉花园的最少水龙头数目
181184
@@ -229,13 +232,17 @@ ranges.length == n + 1
229232
230233#### 思路
231234
232- 贪心策略,我们尽量找到能够覆盖最远(右边)位置的水龙头,并记录它最右覆盖的土地。
235+ 和上面的题思路还是一样的。我们仍然采用贪心策略,继续沿用上面的思路,尽量找到能够覆盖最远(右边)位置的水龙头,并记录它最右覆盖的土地。
236+
237+ 这里我就不多解释了,我们来看下具体的算法,大家自己体会一下有多像。
238+
239+ 算法:
233240
234- - 我们使用 furthest[ i] 来记录经过每一个水龙头 i 能够覆盖的最右侧土地。
235- - 一共有 n+1 个水龙头,我们遍历 n + 1 次。
236- - 对于每次我们计算水龙头的左右边界, [ i - ranges [ i ] , i + ranges [ i ]]
237- - 我们更新左右边界范围内的水龙头的 furthest
238- - 最后从土地 0 开始,一直到土地 n ,记录水龙头数目
241+ - 使用 furthest[ i] 来记录经过每一个水龙头 i 能够覆盖的最右侧土地。一共有 n+1 个水龙头,我们遍历 n + 1 次 。
242+ - 每次都计算并更新水龙头的左右边界 [ i - ranges [ i ] , i + ranges [ i ]] 范围内的水龙头的 furthest
243+ - 最后从土地 0 开始,一直遍历到土地 n ,记录水龙头数目,类似跳跃游戏。
244+
245+ 是不是和上面的题几乎一模一样?
239246
240247#### 代码
241248
@@ -247,23 +254,43 @@ Python3 Code:
247254
248255class Solution :
249256 def minTaps (self , n : int , ranges : List[int ]) -> int :
250- furthest, cnt , cur = [0 ] * n, 0 , 0
251-
257+ furthest, ans , cur = [0 ] * n, 0 , 0
258+ # 预处理
252259 for i in range (n + 1 ):
253- l = max (0 , i - ranges[i])
254- r = min (n, i + ranges[i])
255- for j in range (l, r):
256- furthest[j] = max (furthest[j], r)
257- while cur < n:
258- if furthest[cur] == 0 : return - 1
259- cur = furthest[cur]
260- cnt += 1
261- return cnt
260+ for j in range (max (0 , i - ranges[i]), min (n, i + ranges[i])):
261+ furthest[j] = max (furthest[j], min (n, i + ranges[i]))
262+ # 老套路了
263+ end = last = 0
264+ for i in range (n):
265+ if furthest[i] == 0 : return - 1
266+ last = max (last, furthest[i])
267+ if i == end:
268+ end = last
269+ ans += 1
270+ return ans
262271
263272```
264273
265274** 复杂度分析**
266275
267- - 时间复杂度:时间复杂度取决 l 和 r,也就是说取决于 ranges 数组的值,假设 ranges 的平均大小为 Size 的话,那么时间复杂度为 $O(N * Size)$。
276+ - 时间复杂度:$O(\sum_ {i=1}^{n}R[ i] + n)$,其中 R[ i] 为 ranges[ i] 的区间长度。
277+
278+ - 空间复杂度:$O(n)$。
279+
280+ ## 总结
281+
282+ 极值问题我们可以考虑使用动态规划和贪心,而覆盖类的问题使用动态规划和贪心都是可以的,只不过使用贪心的代码和复杂度通常都会更简单。但是相应地,贪心的难点在于如何证明局部最优解就可以得到全局最优解。通过这几道题的学习,希望你能够明白覆盖类问题的套路,其底层都是一样的。明白了这些, 你回头再去看覆盖类的题目,或许会发现新的世界。
283+
284+ 我整理的 1000 多页的电子书已经开发下载了,大家可以去我的公众号《力扣加加》后台回复电子书获取。
285+
286+ ![ ] ( https://cdn.jsdelivr.net/gh/azl397985856/cdn/2020-10-17/1602928846461-image.png )
287+
288+
289+ ![ ] ( https://cdn.jsdelivr.net/gh/azl397985856/cdn/2020-10-17/1602928862442-image.png )
290+
291+
292+ 大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 37K star 啦。
293+
294+ 大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。
268295
269- - 空间复杂度:我们使用了 furthest 数组, 因此空间复杂度为 $O(N)$。
296+ ![ ] ( https://tva1.sinaimg.cn/large/007S8ZIlly1gfcuzagjalj30p00dwabs.jpg )
0 commit comments