1 条题解
-
0
题目理解
有 个火车站排成一条直线, 条火车轨道覆盖不同的区间。火车从起点 出发,只能单向行驶,但在被多条轨道覆盖的车站可以切换轨道。求所有可能到达的终点车站(即轨道的端点)。
关键点:
- 火车只能停在某条轨道的端点
- 行驶方向一旦确定就不能改变
- 轨道重叠处可以切换轨道
核心思路
关键观察
- 连续覆盖区间:从起点出发,向左右两个方向扩展,火车只能在连续被轨道覆盖的区间内行驶
- 端点特性:停车站必须是某个轨道的端点
- 方向限制:
- 向左行驶时,只能到达左端点
- 向右行驶时,只能到达右端点
算法设计
使用差分数组+贪心扩展的方法:
- 统计覆盖情况:用差分数组记录每个车站被多少条轨道覆盖
- 标记端点:记录哪些车站是轨道的左端点或右端点
- 扩展边界:从起点向左右扩展,找到最大的连续覆盖区间
- 收集答案:在扩展区间内,起点左侧收集左端点,起点右侧收集右端点
算法步骤
步骤详解
-
初始化数组
D[i]:差分数组,用于计算每个车站的覆盖次数lp[i]:标记车站 是否是某个轨道的左端点rp[i]:标记车站 是否是某个轨道的右端点
-
处理输入数据
- 对于每个轨道 :
D[l]++,D[r+1]--(差分标记)lp[l] = 1,rp[r] = 1(标记端点)
- 对于每个轨道 :
-
计算前缀和
- 通过前缀和将差分数组转换为实际的覆盖次数数组
-
扩展边界
- 从起点 向左找到第一个不被覆盖的车站
l - 从起点 向右找到第一个不被覆盖的车站
r - 连续覆盖区间为
- 从起点 向左找到第一个不被覆盖的车站
-
输出答案
- 在 区间内输出所有左端点
- 在 区间内输出所有右端点
复杂度分析
-
时间复杂度:
- 处理 条轨道:
- 前缀和计算:
- 边界扩展:
- 答案收集:
-
空间复杂度:
- 使用三个长度 的数组
代码实现与解析
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false), cin.tie(0); int n, m, k; cin >> n >> m >> k; // 数组定义 vector<int> D(n + 2), lp(n + 2), rp(n + 2); // D: 差分数组,用于计算每个车站被多少轨道覆盖 // lp: 标记左端点,rp: 标记右端点 // 处理每条轨道 for (int i = 0, l, r; i < m && cin >> l >> r; i++) { D[l]++; D[r + 1]--; // 差分标记,表示[l,r]区间覆盖次数+1 lp[l] = 1; // 标记l为左端点 rp[r] = 1; // 标记r为右端点 } // 计算前缀和,得到每个车站的实际覆盖次数 for (int i = 1; i <= n; i++) { D[i] += D[i - 1]; } // 扩展边界:找到从起点k出发的连续覆盖区间 int l = k, r = k; while (l && D[l]) l--; // 向左扩展,找到第一个不被覆盖的车站 while (r <= n && D[r]) r++; // 向右扩展,找到第一个不被覆盖的车站 // 此时连续覆盖区间为 [l+1, r-1] // 收集并输出答案 for (int i = l + 1; i <= r - 1; i++) { // 起点左侧:只能到达左端点(向左行驶) // 起点右侧:只能到达右端点(向右行驶) if ((i < k && lp[i]) || (i > k && rp[i])) { cout << i << " "; } } return 0; }样例验证
样例1
输入: 7 5 4 3 4 4 6 1 3 5 7 4 6 处理过程: - 覆盖区间:[1,3], [3,4], [4,6], [4,6], [5,7] - 连续覆盖区间:[1,7] - 左端点:1,3,4,5;右端点:3,4,6,7 - 起点4左侧的左端点:1,3 - 起点4右侧的右端点:6,7 输出:1 3 6 7算法正确性证明
- 完备性:算法考虑了所有可能的行驶方向和轨道切换情况
- 正确性:只有轨道的端点才能作为停车站,且必须满足方向约束
- 高效性:线性复杂度处理大规模数据
该解法巧妙利用了差分数组和贪心扩展,避免了复杂的图遍历,以简洁高效的方式解决了问题。
- 1
信息
- ID
- 5595
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者