UVa 617 Nonstop Travel

题目描述

菲尔上夜班,每天凌晨 2:002:002:00 准时离开公司的停车场。回家的路是一条直路,路上有一个或多个交通信号灯。菲尔一直想知道,给定每个信号灯的位置和周期,是否存在某个速度,使他能够在不因红灯而加速或减速的情况下顺利通过所有信号灯。你需要编写程序找出所有满足条件的整数速度(英里/小时)。速度范围为 303030606060 英里/小时(含)。他可以在信号灯由黄变红的瞬间或由红变绿的瞬间通过。注意:所有信号灯在凌晨 2:002:002:00 同时开始绿灯周期。

输入格式

输入包含一组或多组数据,每组描述一组交通信号灯,最后以 -1 结束。每组数据的第一行为一个整数 NNNN≤6N \le 6N6),表示信号灯数量。随后 NNN 行,每行包含四个实数 LLLGGGYYYRRR,分别表示信号灯位置(英里)、绿灯时长、黄灯时长、红灯时长(秒)。

输出格式

对于每组数据,输出 Case X: 后接所有可行的整数速度。连续速度应合并为区间形式,如 L-H。若区间长度为 0,则只输出单个数字。区间之间用逗号分隔。如果没有可行速度,输出 No acceptable speeds

样例

输入

1
5.5 40 8 25
3
10.7 10 2 75
12.5 12 5 57
17.93 15 4 67
-1

输出

Case 1: 30, 32-33, 36-38, 41-45, 48-54, 59-60
Case 2: No acceptable speeds.

题目分析

对于每个候选速度 vvv(整数,30≤v≤6030 \le v \le 6030v60),需要判断菲尔是否能在所有信号灯处都遇到绿灯或黄灯(即非红灯)。由于出发时刻固定为 2:002:002:00,且所有信号灯同时开始绿灯周期,因此可以计算从出发到到达第 iii 个信号灯所用的时间 ti=Livt_i = \frac{L_i}{v}ti=vLi 小时,转换为秒:ti=Li×3600vt_i = \frac{L_i \times 3600}{v}ti=vLi×3600 秒。

信号灯的周期为 Pi=Gi+Yi+RiP_i = G_i + Y_i + R_iPi=Gi+Yi+Ri,在一个周期内,绿灯和黄灯的持续时间总和为 Ti=Gi+YiT_i = G_i + Y_iTi=Gi+Yi。若到达时刻 tit_itiPiP_iPi 取模后的值落在 [0,Ti][0, T_i][0,Ti] 内(包括端点),则当前为绿灯或黄灯,可通过;否则为红灯,不可通过。

由于 vvv 仅有 313131 个候选值(303030606060),直接枚举每个速度,依次检查所有信号灯即可。复杂度 O(31×N)O(31 \times N)O(31×N),完全可行。

解题思路

  1. 对于每组数据,读入 NNN 及每个信号灯的 L,G,Y,RL, G, Y, RL,G,Y,R,计算周期 PPP 和绿灯黄灯总时长 TTT
  2. 枚举速度 vvv303030606060
    • 初始化标志 acceptable = true
    • 对每个信号灯 iii,计算到达时刻(秒)的模周期余数 arrived = fmod(L * 3600 / v, P)
    • arrived > T + 1e-7(考虑浮点误差),则说明到达时是红灯,标记为不可行并跳出。
    • 若所有信号灯都通过,则该速度可行。
  3. 收集所有可行速度,然后合并为连续区间输出。
    • 遍历速度 303030606060,若当前速度可行,则开始一个区间,继续往后扫描直到不可行,输出区间起点和终点。
    • 若区间起点等于终点,只输出单个数字。
    • 区间之间用逗号分隔。
  4. 若没有任何可行速度,输出 No acceptable speeds

复杂度分析

  • 枚举速度:313131 个。
  • 每个速度检查最多 666 个信号灯。
  • 总时间复杂度 O(31×N)O(31 \times N)O(31×N),空间复杂度 O(N)O(N)O(N)

代码实现

// Nonstop Travel
// UVa ID: 617
// Verdict: Accepted
// Submission Date: 2016-08-31
// UVa Run Time: 0.000s
//
// 版权所有(C)2016,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>
using namespace std;

struct Signal {
    double L, G, Y, R, P, Rs;
} signals[10];

int main() {
    int n, cases = 0, speed[100];
    while (cin >> n, n >= 0) {
        cout << "Case " << ++cases << ':';
        for (int i = 0; i < n; i++) {
            cin >> signals[i].L >> signals[i].G >> signals[i].Y >> signals[i].R;
            signals[i].P = signals[i].G + signals[i].Y + signals[i].R;
            signals[i].Rs = signals[i].G + signals[i].Y;
        }
        memset(speed, 0, sizeof(speed));
        for (int i = 30; i <= 60; i++) {
            bool acceptable = true;
            for (int j = 0; j < n; j++) {
                double arrived = fmod(signals[j].L * 3600 / i, signals[j].P);
                if (arrived > signals[j].Rs + 1e-7) {
                    acceptable = false;
                    break;
                }
            }
            speed[i] = acceptable ? 1 : 0;
        }
        bool outputed = false;
        int k = 30;
        while (k <= 60) {
            if (speed[k] == 1) {
                if (outputed) cout << ',';
                else outputed = true;
                cout << ' ' << k;
                int length = 0;
                while (k <= 60 && speed[k] == 1)
                    length++, k++;
                if (length > 1) cout << '-' << (k - 1);
            } else k++;
        }
        if (!outputed) cout << " No acceptable speeds.";
        cout << '\n';
    }
    return 0;
}

总结

本题通过枚举所有候选速度,并利用取模运算判断每个信号灯到达时的状态,从而筛选出可行速度。核心是正确计算到达时刻在一个周期内的相对位置,并注意浮点数精度处理。由于候选速度范围很小,暴力枚举是最高效且简洁的方法。输出时需要将连续速度合并为区间,注意格式控制(逗号、空格、换行等)。本题也提醒我们在处理时间与速度的关系时,注意单位换算(小时与秒的转换)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值