LeetCode——1883. 准时抵达会议现场的最小跳过休息次数(Minimum Skips to Arrive at Meeting On Time)[困难]——分析及代码(Java)

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% 的用户。

三、其他

暂无。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值