LeetCode每日一题#1883

文章讨论了如何使用动态规划方法来解决一个关于按时抵达会议的问题,给定剩余时间、道路长度和速度,计算最少需要跳过多少次休息时间以确保准时到达。关键在于构建一个动态规划表格,考虑跳过和不跳过的策略,以最小化总时间。
摘要由CSDN通过智能技术生成

题目:准时抵达会议现场的最小跳过休息次数

描述:

给你一个整数 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
解释:即使跳过所有的休息时间,也无法准时参加会议。

关键词:动态规划

说明:对于这类问题,给定某个数组,我们可以对数组中的任意元素进行操作或者不操作,这时我们很自然的想到了动态规划。例如这道题,题目所说,可以选择 跳过 一些路的休息时间,那也就是意味着,我们有跳过或者不跳过两个选择。

因此构建一个数组dp[n+1][n+1],dp[i][j]表示,第i条道路跳过j次后所用的最短时间。当我们选择跳过时,dp[i][j] = dp[i-1][j-1]+dist[i-1]/speed;当我们选择不跳过时,dp[i][j] = dp[i-1][j]+ceil(dist[i-1]/speed)。两者取最小值。

特别地,当j=0时,只能选择不跳过。当j=i时,只能选择跳过。j不能大于i,否则式子没有意义。

(ceil表示向上取整)

最后我们只需要找到dp[n][i](i<=n)中i的最小值就行了。

const double EPS = 1e-7;
const double Max = 1e20;

int minSkips(int* dist, int distSize, int speed, int hoursBefore) {
    double f[distSize+1][distSize+1];
    for(int i = 0 ; i <= distSize ; i++){
        for(int j = 0 ; j <= distSize ; j++){
            f[i][j] = Max;
        }
    }
    f[0][0] = 0;
    for(int i = 1 ; i<=distSize ;i++){
        for(int j = 0;j<=i ; j++){
            if(j!=i){
                f[i][j] = fmin(f[i][j],ceil(f[i-1][j] + (double)dist[i-1]/speed-EPS));
            }
            if(j!=0){
                f[i][j] = fmin(f[i][j],(f[i-1][j-1]+(double)dist[i-1]/speed));
            }
        }
    }
    for(int i = 0 ; i<= distSize ; i++){
        if(f[distSize][i]<hoursBefore+EPS){
            return i;
        }
    }
    return -1;
}

特别地,里面有关于浮点数的计算法则,我把官解的解释放在下面。

浮点数运算的细节

这一部分非常重要,希望读者仔细阅读。

根据 IEEE 754 标准,浮点数在计算机中存储的精度是有限的,而本题中我们不可避免的会使用「浮点数运算」以及「向上取整」运算,如果强行忽略产生的计算误差,会得到错误的结果。

举一个简单的例子,假设使用的语言中「向上取整」运算的函数为 ceil\texttt{ceil}ceil,下面的运算:

ceil(8.0 + 1.0 / 3 + 1.0 / 3 + 1.0 / 3)\texttt{ceil(8.0 + 1.0 / 3 + 1.0 / 3 + 1.0 / 3)}
ceil(8.0 + 1.0 / 3 + 1.0 / 3 + 1.0 / 3)
应当是 999,而计算机会给出 101010。这是因为浮点数误差导致:

8.0 + 1.0 / 3 + 1.0 / 3 + 1.0 / 3\texttt{8.0 + 1.0 / 3 + 1.0 / 3 + 1.0 / 3}
8.0 + 1.0 / 3 + 1.0 / 3 + 1.0 / 3
计算出的结果约为:

9.000000000000002\texttt{9.000000000000002}
9.000000000000002
向上取整后会得到 101010,产生了错误的答案。

因此我们引入常量 eps\text{eps}eps 表示一个极小值,例如 10−810^{-8}10 
−8
 。在进行「向上取整」运算前,我们将待取整的浮点数减去 eps\text{eps}eps 再进行取整,就可以避免上述的误差。

同时,我们需要说明 eps\text{eps}eps 不会引入其它的问题。在本题中速度最大为 10610^610 
6
 ,时间与速度成反比,那么 eps\text{eps}eps 的粒度只需要高于时间的精度 10−610^{-6}10 
−6
  即可,取 10−710^{-7}10 
−7
  或 10−810^{-8}10 
−8
  都是可行的。

最后在比较 f[n][j]≤hoursBeforef[n][j] \leq \textit{hoursBefore}f[n][j]≤hoursBefore 时,我们也需要将左侧减去 eps\text{eps}eps 或将右侧加上 eps\text{eps}eps,再进行比较。

作者:力扣官方题解
链接:https://leetcode.cn/problems/minimum-skips-to-arrive-at-meeting-on-time/solutions/802990/minimum-skips-to-arrive-at-meeting-on-ti-dp7v/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值