2024.4.19力扣每日一题——准时抵达会议现场的最小跳过休息次数

题目来源

力扣每日一题;题序:1883

我的题解

方法一 动态规划+浮点数精度

参考官方题解
用 f[i][j]表示经过了 dist[0]到 dist[i−1]的 i 段道路,并且跳过了 j 次的最短用时。
在进行状态转移时,考虑最后一段道路 dist[i−1]是否选择跳过:

  • 如果没有跳过,那么状态转移方程为:
          f [ i ] [ j ] = ⌈ f [ i − 1 ] [ j ] + dist [ i − 1 ] speed ⌉ f[i][j] = \left\lceil f[i-1][j] + \frac{\textit{dist}[i-1]}{\textit{speed}} \right\rceil f[i][j]=f[i1][j]+speeddist[i1]
    其中 ⌈x⌉表示将 x向上取整。对于最后一段道路,无需等待到下一个整数小时,但由于题目中给定的 hoursBefore是一个整数,因此在状态转移方程中向上取整是不会影响结果的。
  • 如果跳过,那么状态转移方程为:
        f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + dist [ i − 1 ] speed f[i][j] = f[i-1][j-1] + \frac{\textit{dist}[i-1]}{\textit{speed}} f[i][j]=f[i1][j1]+speeddist[i1]

由于到达的时间尽可能早,因此需要选择这两种转移中的较小值,即:
f [ i ] [ j ] = min ⁡ { ⌈ f [ i − 1 ] [ j ] + dist [ i − 1 ] speed ⌉ , f [ i − 1 ] [ j − 1 ] + dist [ i − 1 ] speed } f[i][j] = \min \left\{ \left\lceil f[i-1][j] + \frac{\textit{dist}[i-1]}{\textit{speed}} \right\rceil, f[i-1][j-1] + \frac{\textit{dist}[i-1]}{\textit{speed}}\right\} f[i][j]=min{f[i1][j]+speeddist[i1],f[i1][j1]+speeddist[i1]}
需要注意的是,当 j=0 时,不能通过「跳过」进行转移;当 j=i 时,不能通过「没有跳过」进行转移;当 j>i 时,无法在 i 段道路内跳过超过 i 次,对应的状态不合法。
当计算完所有状态的值后,只需要找到最小的 j,使得 f[n][j]≤hoursBefore,这个 j即为最少需要跳过的次数。如果不存在这样的 jjj,那么返回 −1。
动态规划的细节

  • 动态规划的边界条件为 f[0][0]=0,表示初始(未走过任何道路)时的时间为 0。
    由于状态转移方程中的取值为 min⁡,因此可以将除了 f[0][0]以外所有的状态置为一个极大值 ∞ \infty ,方便进行转移。
    浮点数精度问题
    引入常量 eps表示一个极小值。在进行「向上取整」运算前,将待取整的浮点数减去 eps再进行取整,就可以避免上述的误差。
    同时,需要说明 eps 不会引入其它的问题。在本题中速度最大为 1 0 6 10^6 106,时间与速度成反比,那么 eps 的粒度只需要高于时间的精度 1 0 − 6 10^{-6} 106即可,取 1 0 − 7 10^{-7} 107 1 0 − 8 10^{-8} 108 都是可行的。
    最后在比较 f[n][j]≤hoursBefore时,也需要将左侧减去 eps或将右侧加上 eps,再进行比较。
    时间复杂度:O( n 2 n^2 n2)
    空间复杂度:O( n 2 n^2 n2)
public int minSkips(int[] dist, int speed, int hoursBefore) {
    int n=dist.length;
    double eps=1e-7;
    double[][] dp=new double[n+1][n+1];
    for(int i=0;i<=n;i++){
        Arrays.fill(dp[i],Double.MAX_VALUE);
    }
    dp[0][0]=0;
    for(int i=1;i<=n;i++){
        //跳跃次数
        for(int j=0;j<=i;j++){
            if(j!=i){
                dp[i][j]=Math.min(dp[i][j],Math.ceil(dp[i-1][j]+(double)dist[i-1]/speed-eps));
            }
            if(j!=0){
                dp[i][j]=Math.min(dp[i][j],dp[i-1][j-1]+(double)dist[i-1]/speed);
            }
        }
    }
    for(int i=0;i<=n;i++){
        if(dp[n][i]<hoursBefore+eps)
            return i;
    }
    return -1;
}
方法二 动态规划+不考虑浮点数精度问题

对所有的除法都乘以speed使其成为整数。将「必须休息并等待,直到下一个整数小时才能开始继续通过下一条道路」,那么当我们将上面的变量都乘以 speed 后,规定应当变更为「必须休息并等待,直到下一个 speed 的倍数小时才能开始继续通过下一条道路

时间复杂度:O( n 2 n^2 n2)
空间复杂度:O( n 2 n^2 n2)

public int minSkips(int[] dist, int speed, int hoursBefore) {
    int n=dist.length;
    long[][] dp=new long[n+1][n+1];
    for(int i=0;i<=n;i++){
        Arrays.fill(dp[i],Long.MAX_VALUE);
    }
    dp[0][0]=0;
    for(int i=1;i<=n;i++){
        //跳跃次数
        for(int j=0;j<=i;j++){
            if(j!=i){
                dp[i][j] = Math.min(dp[i][j], ((dp[i - 1][j] + dist[i - 1] - 1) / speed + 1) * speed);
            }
            if(j!=0){
                dp[i][j]=Math.min(dp[i][j],dp[i-1][j-1]+dist[i-1]);
            }
        }
    }
    for(int i=0;i<=n;i++){
        if(dp[n][i]<=(long)hoursBefore*speed)
            return i;
    }
    return -1;
}

有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈😄~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜菜的小彭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值