[CSP-J 2026] 旅游 / travel(民间数据)

GitHub跳转原题关系图返回列表

把车票拆成车厢、排号、过道侧、侧内下标四项,按题目给的四个条件逐层判断。

OJ: luogu

题目 ID: P17827

难度:入门

标签:模拟字符串

创建: 2026-10-31 18:55

更新: 2026-10-31 20:14

形式化题目

一个座位由四元组 (c,r,s,p)(c, r, s, p) 描述:cc 是车厢号,rr 是排号,ss 是所在过道侧,pp 是该座位在这一侧序列中的下标。原题把座位写成“两位排号 + 一个字母”(如 03C),字母决定过道侧 ss,字母在该侧序列中的位置决定下标 pp。

同一节二等座车厢的每一排固定排成两段,两个过道侧之间隔着走道。下图就是一排的排布,以及两种编号方式的差别:

一排座位:过道把 A B C 与 D F 切成两段,按侧编号 0 1 2 / 0 1,统一编号会让 C 与 D 只差 1

看图时注意三件事:一是 A、B、C 与 D、F 各自连成一段,中间那条虚线是过道,不是座位;二是同侧相邻的三组(A 与 B、B 与 C、D 与 F)在图上用绿线标出,而 C 与 D 中间没画绿线;三是两条编号行——按侧编号时 C 是 2、D 是 0,统一编号时它们是 2 和 3,只差 1。

给定两个座位 u=(c1,r1,s1,p1)u = (c_1, r_1, s_1, p_1)、v=(c2,r2,s2,p2)v = (c_2, r_2, s_2, p_2),要求对下面四个命题依次给出真值:

  1. 同车厢:c1=c2c_1 = c_2;
  2. 同排:c1=c2c_1 = c_2 且 r1=r2r_1 = r_2;
  3. 同排同侧:c1=c2c_1 = c_2、r1=r2r_1 = r_2 且 s1=s2s_1 = s_2;
  4. 相邻:第 3 条成立且 ∣p1−p2∣=1|p_1 - p_2| = 1。

四个答案按顺序拼成一个长度为 4 的 01 串。

正解

思路

一句话本质:把“座位字母在字母表上的顺序”换成“在哪一侧 + 在这一侧从左数第几个”,相邻就退化成两个整数相差 1。

要输出的这四个 0/1 是并列的吗?

不是。第 1 问只看车厢号;第 2 问是“车厢相同并且排号相同”;第 3 问是“同一排并且在过道同一边”;第 4 问是“同一边并且在这一侧的序列里挨着”。后一问都以前一问为前提。样例 #5 的 02 01A 与 03 01A 输出 0000 就是证据:两张票的排号都是 01,可车厢不同,第 2 个答案照样是 0。

所以四个答案是一条合取链,前三问只是把字符串拿来比较,真正要想的只有最后一问。

那“相邻”到底该怎么判?

看上面的图:一排是 A B C | D F,过道把这一排切成左右两段。同一段里座位有先后,跨过道两边就没有“挨着”这回事。

为什么不能把五个字母排成一条直线,用下标差 1 判相邻?

因为过道不是座位,却结结实实占了 C 和 D 之间的位置。如果按 A=0, B=1, C=2, D=3, F=4 编号,C 与 D 只差 1,就会被判成相邻;而样例 #3 给出 01C 与 01D 的正确答案是 1100,第 4 位是 0。一条直线的下标把过道那段距离抹平了,判据就不再等价于题面定义。

按侧分开编号之后,判据写成什么?

每个座位记两个数:side 说在哪一侧(左 0、右 1),p 说在这一侧从左数第几个(A B C 是 0 1 2,D F 是 0 1)。相邻就是四个条件同时成立:同车厢、同排、同侧、pp 相差 1。

这个等价直接由题面定义展开,两个方向都不需额外论证:题面写“两个座位相邻,当且仅当在同一节车厢的同一排,位于过道的同一侧,且在该侧的座位序列中相邻”,前三个就是 same_car、same_row、same_side,而“在一维序列中相邻”就是下标相差 1。

顺带一个容易被问到的点:并不需要把整节车厢的座位表存下来。一排的排布对所有二等座车厢都一样,座位和“车厢 + 排 + 侧 + 序号”一一对应,车票又保证两人都在二等座车厢里,所以四个数对上,物理位置就唯一确定。

排号和车厢号都以字符串形式给出(03、02 这种带前导零的写法),本题只需判断是否相等,直接按字符串比就行,不必转成整数。

代码里就两个小函数:seat_side 回答“在过道哪一边”,seat_pos 回答“在这一边从左数是第几个”,剩下部分照着四个条件依次写,最后四个 if 依次输出 1 或 0。

代码

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-31 18:55
 * update_at: 2026-10-31 19:20
 */
// main.cpp:把座位字母换算成「过道的哪一边 + 这一边从左数第几个」,
// 再按题目的四个条件从外到内逐层判断。
#include <bits/stdc++.h>
using namespace std;

// 座位在这一侧序列里从左数的位置,从 0 开始
// 过道左边是 A B C,分别对应 0 1 2;过道右边是 D F,分别对应 0 1
int seat_pos(char c) {
    if (c == 'A') return 0;
    if (c == 'B') return 1;
    if (c == 'C') return 2;
    if (c == 'D') return 0;
    return 1;   // 剩下的只可能是 'F'
}

// 座位在过道的哪一边:左边是 0,右边是 1
int seat_side(char c) {
    if (c == 'D' || c == 'F') return 1;
    return 0;
}

int main() {
    string car1, seat1, car2, seat2;   // 两张车票:车厢号、座位号
    cin >> car1 >> seat1 >> car2 >> seat2;

    char ch1 = seat1[2];   // 小 R 的座位字母,比如 01A 里的 'A'
    char ch2 = seat2[2];   // 小 X 的座位字母

    // 第 1 问:车厢号是否相同
    bool same_car = (car1 == car2);
    // 第 2 问:车厢相同,并且排号(座位号的前两位)也相同
    bool same_row = same_car && seat1[0] == seat2[0] && seat1[1] == seat2[1];
    // 第 3 问:同一排,并且在过道的同一边
    bool same_side = same_row && seat_side(ch1) == seat_side(ch2);
    // 第 4 问:同一边,并且在这一边的序列里挨着(位置只差 1)
    int p1 = seat_pos(ch1);
    int p2 = seat_pos(ch2);
    bool near_seat = same_side && (p1 + 1 == p2 || p2 + 1 == p1);

    if (same_car) cout << 1; else cout << 0;
    if (same_row) cout << 1; else cout << 0;
    if (same_side) cout << 1; else cout << 0;
    if (near_seat) cout << 1; else cout << 0;
    cout << '\n';
    return 0;
}

复杂度

四个判断都是常数次字符比较,时间复杂度 O(1)O(1),空间复杂度 O(1)O(1)。

总结

这道题答案只值几十行代码,值得带走的是两次换视角:

  • 先分清哪一问需要新想法:四个答案是一条合取链,前三问只是把字符串拿来比,唯一需要动脑的是“相邻”。先把前面几问照定义写完,只留最后一问去想,比一上来就盯着四行输出去设计要省力。
  • 判据要跟着结构走:座位是一条被过道切断的线。切断之后,“离得多近”不再是全局坐标的差,而是“先看在哪一段,再看段内第几个”。换成这种二元描述,判据就是同段且组内下标差 1。

这条经验可以直接搬:车厢两侧、棋盘的黑白格分组、环上被断点切开的位置,只要结构里存在“不属于任何点的间隙”,用全局一维序号量距离就会在间隙处出错,正确做法是先分组再比组内位置。

造对拍数据时,“跨过道”“跨排”“同排号但不同车厢”这几类必须显式覆盖。如果只随机座位,很容易一直抽不到 01C 与 01D,而这恰好是统一序号写法的唯一失效点。

相关 rbook 文章

做这题用到的知识点在 rbook 算法书 上有专门章节,卡住时可以直接跳过去看:

知识点 对应文章 用在哪
字符串与字符下标 《字符串》 车票里 s[0]、s[1] 是排号,s[2] 是座位字母,本题只做相等比较
复杂度记号 《第一章 · 算法基础概念》 本题是 O(1)O(1),正文末尾那套时间复杂度写法

本题的核心技巧是先把一排座位按过道切成两段,再用“哪一段 + 段内第几个”代替全局序号。rbook 目前没有讲这个技巧的专文。