LC每日一题|20240419 - 1883. 准时抵达会议现场的最小跳过休息次数
给你一个整数
hoursBefore,表示你要前往会议所剩下的可用小时数。要想成功抵达会议现场,你必须途经n条道路。道路的长度用一个长度为n的整数数组dist表示,其中dist[i]表示第i条道路的长度(单位:千米)。另给你一个整数speed,表示你在道路上前进的速度(单位:千米每小时)。当你通过第
i条路之后,就必须休息并等待,直到 下一个整数小时 才能开始继续通过下一条道路。注意:你不需要在通过最后一条道路后休息,因为那时你已经抵达会议现场。
- 例如,如果你通过一条道路用去
1.4小时,那你必须停下来等待,到2小时才可以继续通过下一条道路。如果通过一条道路恰好用去2小时,就无需等待,可以直接继续。然而,为了能准时到达,你可以选择 跳过 一些路的休息时间,这意味着你不必等待下一个整数小时。注意,这意味着与不跳过任何休息时间相比,你可能在不同时刻到达接下来的道路。
- 例如,假设通过第
1条道路用去1.4小时,且通过第2条道路用去0.6小时。跳过第1条道路的休息时间意味着你将会在恰好2小时完成通过第2条道路,且你能够立即开始通过第3条道路。返回准时抵达会议现场所需要的 最小跳过次数 ,如果 无法准时参会 ,返回
-1。
提示:
n == dist.length1 <= n <= 10001 <= dist[i] <= 10^51 <= speed <= 10^61 <= hoursBefore <= 10^7
题目等级:Hard
解题思路
首先,如果我们不休息时间都不够用的话,那么应该直接返回 -1 。
然后,我们可以定义一个二维dp数组dp[i][j],表示在跳过最多 i 次休息的情况下走完道路 j 所消耗的最短时间。
对于其他 dp[i][j] ,易知存在以下转移:
dp[i - 1][j - 1] + dist[j] / speed: 完成j - 1时跳过休息Math.ceil(dp[i][j - 1]) + + dist[j] / speed: 完成j - 1前已经休息过,此处不能跳过休息
显然这里我们应该求其较小值,然后找到满足 j == dist.size - 1 且时间小于等于 hoursBefore 的最小的 i 即可。
但是使用浮点数可能会带来精度问题,万一debug时踩坑会使我本不富裕的脑细胞雪上加霜。
所以这里我们就干脆不除以 speed 了,这样 dp[i][j] 就定义成了 该段时间内如果不休息能跑多远。
AC代码
class Solution {
fun minSkips(dist: IntArray, speed: Int, hoursBefore: Int): Int {
val max = 1L * speed * hoursBefore
if (dist.sum().toLong() > max) return -1 //所有休息时间都跳过,如果还是不够,直接返回-1
val dp = Array<IntArray>(dist.size) { IntArray(dist.size) }
dp[0][0] = dist[0]
for (i in 1 until dp.size) {
dp[i][0] = dist[0]
dp[0][i] = (dp[0][i - 1] + speed - 1) / speed * speed + dist[i]
}
if (dp[0].last().toLong() <= max) return 0
for (i in 1 until dp.size) {
for (j in 1 until dp.size) {
dp[i][j] = Math.min(
dp[i - 1][j - 1],
(dp[i][j - 1] + speed - 1) / speed * speed
) + dist[j]
}
if (dp[i].last().toLong() <= max) return i
}
return -1
}
}
时间复杂度:O(N^2)
空间复杂度:O(N^2)
N为dist的大小
碎碎念
我竟然AC了一道DP!!!!!!