题目描述
给你一个整数 hoursBefore ,表示你要前往会议所剩下的可用小时数。要想成功抵达会议现场,你必须途经 n 条道路。道路的长度用一个长度为 n 的整数数组 dist 表示,其中 dist[i] 表示第 i 条道路的长度(单位:千米)。另给你一个整数 speed ,表示你在道路上前进的速度(单位:千米每小时)。
当你通过第 i 条路之后,就必须休息并等待,直到 下一个整数小时 才能开始继续通过下一条道路。注意:你不需要在通过最后一条道路后休息,因为那时你已经抵达会议现场。
例如,如果你通过一条道路用去 1.4 小时,那你必须停下来等待,到 2 小时才可以继续通过下一条道路。如果通过一条道路恰好用去 2 小时,就无需等待,可以直接继续。
然而,为了能准时到达,你可以选择 跳过 一些路的休息时间,这意味着你不必等待下一个整数小时。注意,这意味着与不跳过任何休息时间相比,你可能在不同时刻到达接下来的道路。
例如,假设通过第 1 条道路用去 1.4 小时,且通过第 2 条道路用去 0.6 小时。跳过第 1 条道路的休息时间意味着你将会在恰好 2 小时完成通过第 2 条道路,且你能够立即开始通过第 3 条道路。
返回准时抵达会议现场所需要的 最小跳过次数 ,如果 无法准时参会 ,返回 -1 。
示例 1:
输入:dist = [1,3,2], speed = 4, hoursBefore = 2
输出:1
解释:
不跳过任何休息时间,你将用 (1/4 + 3/4) + (3/4 + 1/4) + (2/4) = 2.5 小时才能抵达会议现场。
可以跳过第 1 次休息时间,共用 ((1/4 + 0) + (3/4 + 0)) + (2/4) = 1.5 小时抵达会议现场。
注意,第 2 次休息时间缩短为 0 ,由于跳过第 1 次休息时间,你是在整数小时处完成通过第 2 条道路。
思路
动态规划,建立变量为路径数和跳过休息次数,值为花费的最小时间的动态方程,这里更新的顺序和官解不同,可以提前返回最小不休息次数。比如,极端的情况是,当时间足够大时,不需要休息,返回0。
另外,由于最后一段路程不需要休息,因此可以直接将其从从路径列表中删去,同时在可用时间中减去相应所需要花费的时间即可。对于小数的问题,可以将时间延展(相当于开上帝视角给世界开了慢速),这样问题可以等效于,将速度变为1,同时可用的时间乘上了原来的速度,同时每一段如果不跳过休息所需的时间应该以原来的速度为模向上取整。
复杂度
最优时间复杂度O(n),最差时间复杂度O(n^2
代码
class Solution:
def minSkips(self, dist: List[int], speed: int, hoursBefore: int) -> int:
n = len(dist)
hoursBefore *= speed
hoursBefore -= dist.pop()
dp = [[float('inf')]*n for _ in range(n)]
dp[0][0] = 0
align = lambda x: x + (speed-x%speed)%speed
for road in range(1,n): dp[road][0] = align(dp[road-1][0] + dist[road-1])
if(dp[n-1][0] <= hoursBefore): return 0
for skip_num in range(1,n):
for road in range(skip_num,n):
d1 = dp[road-1][skip_num-1] + dist[road-1]
d2 = align(dp[road-1][skip_num] + dist[road-1])
dp[road][skip_num] = min(d1,d2)
if(road == n-1 and dp[road][skip_num] <= hoursBefore): return skip_num
return -1
心得
这道题做了很久,一些关键的问题都有了思路,但就是动态方程没写出来。问题在于,一直想构建变量为时间,值为次数的动态方程,这样一来问题很复杂,同时会陷入在dp时,每一段路都需要考虑是否休息,这样一来无可避免时间复杂度为O(2^n),丧失了dp的意义。其实在一开始时,脑海中闪过了正解的方法,但由于复杂度是O(n^2),就没有考虑。
以后在想动态规划时,应该仔细考虑哪些是自变量哪些是值的问题。