LeetCode——1883. 准时抵达会议现场的最小跳过休息次数[Minimum Skips to Arrive at Meeting On Time][困难]——分析及代码[Java]
一、题目
给你一个整数 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 条道路。
示例 2:
输入:dist = [7,3,5,5], speed = 2, hoursBefore = 10
输出:2
解释:
不跳过任何休息时间,你将用 (7/2 + 1/2) + (3/2 + 1/2) + (5/2 + 1/2) + (5/2) = 11.5 小时才能抵达会议现场。
可以跳过第 1 次和第 3 次休息时间,共用 ((7/2 + 0) + (3/2 + 0)) + ((5/2 + 0) + (5/2)) = 10 小时抵达会议现场。
示例 3:
输入:dist = [7,3,5,5], speed = 1, hoursBefore = 10
输出:-1
解释:即使跳过所有的休息时间,也无法准时参加会议。
提示:
- n == dist.length
- 1 <= n <= 1000
- 1 <= dist[i] <= 10^5
- 1 <= speed <= 10^6
- 1 <= hoursBefore <= 10^7
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-skips-to-arrive-at-meeting-on-time
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、分析及代码
1. 动态规划
(1)思路
设计一个动态规划数组 dp,dp[i][j] 表示途径 i 条道路跳过 j 次休息情况下的最小用时,遍历过程中根据上一道路是否休息选取最小值,结合状态转移方程求解。
需要注意的是,由于浮点数计算及向上取整可能产生误差,需进行专门处理,详见浮点数运算的细节。
(2)代码
class Solution {
public int minSkips(int[] dist, int speed, int hoursBefore) {
int n = dist.length;
double eps = 1e-8, inf = 1e10;//eps用于避免浮点数计算误差导致向上取整后出现错误,inf作为最大值初始化动态规划数组
double[][] dp = new double[n + 1][n + 1];//dp[i][j]表示途径i条道路跳过j次休息时的最小用时
for (int i = 0; i <= n; i++)
Arrays.fill(dp[i], inf);
//动态规划
dp[0][0] = 0;
for (int i = 1; i <= n; i++) {
double t = (double)dist[i - 1] / speed;//第i条道路耗时
dp[i][0] = Math.ceil(dp[i - 1][0] - eps) + t;//单独计算不跳过休息时的值
dp[i][i] = dp[i - 1][i - 1] + t;//单独计算跳过所有休息时的值
for (int j = i - 1; j > 0; j--)
dp[i][j] = Math.min(Math.ceil(dp[i - 1][j] - eps) + t, dp[i - 1][j - 1] + t);//根据上一道路是否休息,确定最小值
}
for (int i = 0; i <= n; i++)//遍历寻找边界,也可用二分进一步优化
if (dp[n][i] <= hoursBefore + eps)
return i;
return -1;
}
}
(3)结果
执行用时 :82 ms,在所有 Java 提交中击败了 78.70% 的用户;
内存消耗 :59.1 MB,在所有 Java 提交中击败了 18.05% 的用户。
三、其他
暂无。