• 描述

    给定若干个子区间,至少需要选择多少个区间,能够覆盖一个大区间?

    • 例题

    在 x 轴上有一个一维的花园。花园长度为 n,从点 0 开始,到点 n 结束。
    花园里总共有 n + 1 个水龙头,分别位于 [0, 1, …, n] 。
    给你一个整数 n 和一个长度为 n + 1 的整数数组 ranges ,其中 ranges[i] (下标从 0 开始)表示:如果打开点 i 处的水龙头,可以灌溉的区域为 [i - ranges[i], i + ranges[i]] 。
    请你返回可以灌溉整个花园的 最少水龙头数目 。如果花园始终存在无法灌溉到的地方,请你返回 -1 。注意:花园区域是连续的。
    题目来源:Leetcode1326题
    题目链接:https://leetcode-cn.com/problems/minimum-number-of-taps-to-open-to-water-a-garden/
    参考解答:
    贪心算法
    最小区间覆盖 - 图1

    1. class Solution:
    2. def minTaps(self, n: int, ranges: List[int]) -> int:
    3. rightMost = [0] * len(ranges)
    4. for i, d in enumerate(ranges):
    5. l, r = max(0, i - d), min(n, i + d)
    6. rightMost[l] = max(rightMost[l], r)
    7. farthest, current, ans = 0, 0, 0
    8. for i in range(n):
    9. farthest = max(farthest, rightMost[i])
    10. if farthest == i:
    11. return -1
    12. if current == i:
    13. current = farthest
    14. ans += 1
    15. return ans

    动态规划
    最小区间覆盖 - 图2

    1. class Solution:
    2. def minTaps(self, n: int, ranges: List[int]) -> int:
    3. prev = [x for x in range(n + 1)]
    4. for i in range(n + 1):
    5. l = max(i - ranges[i], 0)
    6. r = min(i + ranges[i], n)
    7. prev[r] = min(prev[r], l)
    8. BIG = 2**30
    9. dp = [0] + [BIG] * n
    10. for i in range(1, n + 1):
    11. for j in range(prev[i], i):
    12. if dp[j] != BIG:
    13. dp[i] = min(dp[i], dp[j] + 1)
    14. return -1 if dp[n] == BIG else dp[n]
    • 变式题
      • 视频拼接

    你将会获得一系列视频片段,这些片段来自于一项持续时长为 T 秒的体育赛事。这些片段可能有所重叠,也可能长度不一。
    视频片段 clips[i] 都用区间进行表示:开始于 clips[i][0] 并于 clips[i][1] 结束。我们甚至可以对这些片段自由地再剪辑,例如片段 [0, 7] 可以剪切成 [0, 1] + [1, 3] + [3, 7] 三部分。
    我们需要将这些片段进行再剪辑,并将剪辑后的内容拼接成覆盖整个运动过程的片段([0, T])。返回所需片段的最小数目,如果无法完成该任务,则返回 -1 。
    题目来源:Leetcode1024题
    题目链接:https://leetcode-cn.com/problems/video-stitching
    参考解答:
    贪心算法

    1. class Solution:
    2. def videoStitching(self, clips: List[List[int]], T: int) -> int:
    3. rightMost = list(range(T + 1))
    4. for i in range(len(clips)):
    5. l, r = min(clips[i][0], T), clips[i][1]
    6. rightMost[l] = max(rightMost[l], r)
    7. farthest, ans, current = 0, 0, 0
    8. for i in range(T): # 需要注意,for循环从[0,T-1],不能到T。
    9. farthest = max(farthest, rightMost[i])
    10. if farthest <= i:
    11. return -1
    12. if i == current:
    13. current = farthest
    14. ans += 1
    15. return ans

    动态规划

    1. class Solution:
    2. def videoStitching(self, clips: List[List[int]], T: int) -> int:
    3. dp = [T + 1] * (T + 1)
    4. dp[0] = 0
    5. for i in range(0, T+1):
    6. for c in clips:
    7. if c[0] <= i and c[1] >= i:
    8. dp[i] = min(dp[i], dp[c[0]]+1)
    9. return -1 if dp[T] == T + 1 else dp[T]
    • 跳跃游戏II

    给定一个非负整数数组,你最初位于数组的第一个位置。
    数组中的每个元素代表你在该位置可以跳跃的最大长度。
    你的目标是使用最少的跳跃次数到达数组的最后一个位置。
    题目来源:Leetcode45题
    题目链接:https://leetcode-cn.com/problems/jump-game-ii
    参考解答:

    1. class Solution:
    2. def jump(self, nums: List[int]) -> int:
    3. farthest, ans, current = 0, 0, 0
    4. for i in range(0, len(nums) - 1): # 注意,范围[0, len(nums)-1)
    5. farthest = max(farthest, i + nums[i])
    6. if i == current:
    7. current = farthest
    8. ans += 1
    9. return ans