1 条题解
-
0
题目详解:B. 公交车上的座位安排
问题理解
我们有 个座位排成一行,编号 到 。
乘客按顺序上车,每个乘客必须遵守:- 第 1 个乘客:可以任意坐(因为车上还没有人)。
- 第 2 个及以后的乘客:必须坐在某个至少有一个相邻座位已被占用的空座位上。
换句话说,当第 ()个乘客坐下时,他的座位 必须满足:
座位 或 中至少有一个已经被人坐了(即在前面的乘客中已经出现过)。我们已知所有乘客按时间顺序坐下的座位编号数组 ,需要判断是否所有人都遵守了规则。
关键观察
如果我们把已经坐下的座位看作一个连续的区间吗?不一定,但我们可以注意到一个重要性质:
在任何时刻,所有已经被占用的座位一定构成一个连续的区间(没有间隔)。
为什么?
- 第 1 个人坐了一个座位,区间就是 ,是连续的。
- 第 2 个人必须坐在 或 ,这会把区间向左或向右扩展 1 个位置,区间仍然连续。
- 第 3 个人必须坐在当前区间的左端点 或右端点 ,否则就会产生空座位被跳过的情况。
- 依此类推,任何时候,所有已占座位一定是一个连续的区间 。
算法思路
我们可以模拟这个过程:
- 第一个乘客坐在 ,当前已占座位的区间为 。
- 对于第 到 个乘客:
- 如果 ,说明他坐在当前区间左侧,更新 。
- 否则如果 ,说明他坐在当前区间右侧,更新 。
- 否则, 既不是 也不是 ,说明他坐在了一个非相邻的位置,违反了规则,输出
"NO"。
- 如果所有乘客都满足条件,输出
"YES"。
示例分析
示例 1:
- 第 1 人坐 ,区间 。
- 第 2 人坐 ,,合法,区间变为 。
- 第 3 人坐 , 不是 ,也不是 ,不合法,输出
"NO"。
示例 2:
- 第 1 人坐 ,区间 。
- 第 2 人坐 ,,合法,区间 。
- 第 3 人坐 ,,合法,区间 。
- 输出
"YES"。
时间复杂度
- 每个测试用例只遍历一次数组,时间复杂度 。
- 所有测试用例的 总和 ,总复杂度 ,足够快。
正确性证明
- 必要性:如果某人坐在非当前区间端点的相邻位置,那么他左右邻居要么是空(如果他在区间内部且不是端点),要么有一个邻居是空且另一个邻居是更早的座位,但根据规则,他必须至少有一个已占邻居。实际上,我们的判断 或 正是“至少有一个邻居被占”的唯一可能,因为当前已占座位是一个连续区间,只有两端外侧的座位才恰好有一个邻居被占,中间的座位虽然有两个邻居被占,但那些座位已经被占了,不可能再坐。
- 充分性:如果每次新乘客都坐在当前区间的左端 或右端 ,那么他显然有一个邻居(原来的端点)已被占,符合规则,且新区间仍连续。
标程代码解读
#include <bits/stdc++.h> using namespace std; void solve() { int n; cin >> n; vector<int> a(n); for (auto &i : a) cin >> i; int left = a[0], right = a[0]; // 当前已占座位的区间 for (int i = 1; i < n; i++) { if (a[i] + 1 == left) // 坐在当前区间左侧 left = a[i]; else if (a[i] - 1 == right) // 坐在当前区间右侧 right = a[i]; else { cout << "NO\n"; return; } } cout << "YES\n"; } int main() { int t; cin >> t; while (t--) solve(); return 0; }这个解法非常简洁,利用了“已占座位始终连续”这一关键性质,只需要维护左右边界即可,不需要额外的布尔数组。
- 1
信息
- ID
- 6298
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者