把车票拆成车厢、排号、过道侧、侧内下标四项,按题目给的四个条件逐层判断。
OJ: luogu
题目 ID: P17827
难度:入门
标签:模拟字符串
创建: 2026-10-31 18:55
更新: 2026-10-31 20:14
形式化题目
一个座位由四元组 03C),字母决定过道侧
同一节二等座车厢的每一排固定排成两段,两个过道侧之间隔着走道。下图就是一排的排布,以及两种编号方式的差别:
看图时注意三件事:一是 A、B、C 与 D、F 各自连成一段,中间那条虚线是过道,不是座位;二是同侧相邻的三组(A 与 B、B 与 C、D 与 F)在图上用绿线标出,而 C 与 D 中间没画绿线;三是两条编号行——按侧编号时 C 是 2、D 是 0,统一编号时它们是 2 和 3,只差 1。
给定两个座位
- 同车厢:
; - 同排:
且 ; - 同排同侧:
、 且 ; - 相邻:第 3 条成立且
。
四个答案按顺序拼成一个长度为 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)。相邻就是四个条件同时成立:同车厢、同排、同侧、
这个等价直接由题面定义展开,两个方向都不需额外论证:题面写“两个座位相邻,当且仅当在同一节车厢的同一排,位于过道的同一侧,且在该侧的座位序列中相邻”,前三个就是 same_car、same_row、same_side,而“在一维序列中相邻”就是下标相差 1。
顺带一个容易被问到的点:并不需要把整节车厢的座位表存下来。一排的排布对所有二等座车厢都一样,座位和“车厢 + 排 + 侧 + 序号”一一对应,车票又保证两人都在二等座车厢里,所以四个数对上,物理位置就唯一确定。
排号和车厢号都以字符串形式给出(03、02 这种带前导零的写法),本题只需判断是否相等,直接按字符串比就行,不必转成整数。
代码里就两个小函数:seat_side 回答“在过道哪一边”,seat_pos 回答“在这一边从左数是第几个”,剩下部分照着四个条件依次写,最后四个 if 依次输出 1 或 0。
代码
/**
* 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;
}复杂度
四个判断都是常数次字符比较,时间复杂度
总结
这道题答案只值几十行代码,值得带走的是两次换视角:
- 先分清哪一问需要新想法:四个答案是一条合取链,前三问只是把字符串拿来比,唯一需要动脑的是“相邻”。先把前面几问照定义写完,只留最后一问去想,比一上来就盯着四行输出去设计要省力。
- 判据要跟着结构走:座位是一条被过道切断的线。切断之后,“离得多近”不再是全局坐标的差,而是“先看在哪一段,再看段内第几个”。换成这种二元描述,判据就是同段且组内下标差 1。
这条经验可以直接搬:车厢两侧、棋盘的黑白格分组、环上被断点切开的位置,只要结构里存在“不属于任何点的间隙”,用全局一维序号量距离就会在间隙处出错,正确做法是先分组再比组内位置。
造对拍数据时,“跨过道”“跨排”“同排号但不同车厢”这几类必须显式覆盖。如果只随机座位,很容易一直抽不到 01C 与 01D,而这恰好是统一序号写法的唯一失效点。
相关 rbook 文章
做这题用到的知识点在 rbook 算法书 上有专门章节,卡住时可以直接跳过去看:
| 知识点 | 对应文章 | 用在哪 |
|---|---|---|
| 字符串与字符下标 | 《字符串》 | 车票里 s[0]、s[1] 是排号,s[2] 是座位字母,本题只做相等比较 |
| 复杂度记号 | 《第一章 · 算法基础概念》 | 本题是 |
本题的核心技巧是先把一排座位按过道切成两段,再用“哪一段 + 段内第几个”代替全局序号。rbook 目前没有讲这个技巧的专文。
:::