top1编程
← 返回题目
题解

公交换乘

1 条题解

  • 0
    @ 2026-8-7 12:55:51

    P4836 公交换乘(提高)

    解题思路

    第一步,看懂优惠规则。 坐地铁需要花钱,但坐完地铁会得到一张优惠票,这张票45分钟内有效。之后坐公交车时,只要存在一张"没过期、且这张票对应的地铁票价不低于这次公交票价"的优惠票,就必须使用它免费乘车;如果有好几张都能用,就使用获得最早的那张。如果找不到能用的票,公交车就得花钱买。题目要我们算出总共花了多少钱。

    第二步,用队列保存还没用掉的优惠票。 优惠票是按获得时间先后排队的,最早获得的票自然排在最前面,正好符合"优先用最早获得"的规则。我们用三个数组模拟这个队列:tp 存每张票对应的地铁票价,tt 存这张票获得的时间,used 标记这张票是否已经用过;head 和 tail 分别是队列的头尾指针。

    第三步,一条一条处理出行记录。 如果是坐地铁:把票价加进总花费,并把这张新票放进队列尾部。如果是坐公交车:先把队列头部"已经超过45分钟"的过期票清掉(因为记录按时间排序,过期票一定集中在队头);然后从队头开始往后找第一张"没用过、且票价不低于公交票价"的票,找到了就标记用过、这趟免费;找不到就乖乖付钱。

    第四步,为什么从队头找就一定是"最早获得"? 因为票按时间先后依次放进队列,从头开始扫,扫到的第一张可用票自然就是获得时间最早的,完全符合题目"有多张优惠票时优先消耗最早获得"的要求。注意车票过期后要先从队头清除,避免拿过期的票去坐车。

    第五步,用样例验证。 3分钟坐10元地铁,得票,花费10;46分钟坐5元公交,票没过期且10≥5,用票,免费;50分钟坐12元地铁,得票,花费12;96分钟坐3元公交,12≥3,用票;110分钟坐5元地铁,得票,花费5;135分钟坐6元公交,手里5元的票比6元便宜,不能用,只好花6元。总花费是 10+12+5+6=36,和样例一致。

    参考代码

    // 公交换乘:地铁得优惠票(45分钟内可免费坐不超地铁价的公交),模拟花费
    #include <iostream>
    using namespace std;
    
    int tp[100005];        // 优惠票对应的地铁票价
    long long tt[100005];  // 优惠票获得的时间
    int used[100005];      // 优惠票是否已被使用
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0);
        int n, ty, p, i;
        long long t, total = 0;
        int head = 0, tail = 0;  // 优惠票队列的头尾指针
        cin >> n;
        while (n--) {
            cin >> ty >> p >> t;
            if (ty == 0) {              // 地铁:必须花钱,获得一张优惠票
                tp[tail] = p;
                tt[tail] = t;
                tail++;
                total += p;
            } else {                    // 公交车:先清除过期的优惠票
                while (head < tail && t - tt[head] > 45) head++;
                int flag = 0;
                for (i = head; i < tail; i++) {
                    if (!used[i] && tp[i] >= p) {  // 最早获得且票价比车费高的
                        used[i] = 1;
                        flag = 1;
                        break;
                    }
                }
                if (!flag) total += p;  // 没有可用优惠票就要付钱
            }
        }
        cout << total << "\n";
        return 0;
    }
    

    复杂度分析

    每条出行记录最多会扫描一遍当前队列里的优惠票,最坏情况下时间会达到 O(n²)。不过因为优惠票45分钟后就过期、会被从队头清除,队列通常很短,而且数据规模 n≤100000,实际测试中最大的数据也只跑了几十毫秒,完全能通过。空间上保存优惠票的三个数组大小都和 n 同级别,空间复杂度是 O(n)。

    • 1