PTA天梯赛L2-014题解:用STL set实现列车调度,原来贪心算法还能这么玩
用STL set玩转列车调度从贪心算法到竞赛实战想象你站在火车站的控制塔上眼前是错综复杂的铁轨网络一列列火车正等待调度。如何用最少的轨道让所有列车按特定顺序离开这不仅是铁路调度员的难题更是算法竞赛中经典的贪心算法应用题。今天我们就以PTA天梯赛L2-014题为切入点揭秘STL set在这一场景下的神奇威力。1. 问题本质与算法选择列车调度问题的核心可以抽象为序列覆盖问题给定一个数字序列我们需要将其划分为尽可能少的递减子序列。这与计算机科学中著名的**最长上升子序列(LIS)**问题有着微妙的联系。为什么选择贪心算法因为这个问题满足贪心选择性质局部最优性每次为当前列车选择最合适的轨道无后效性当前选择不会影响之前的选择结果全局最优解局部最优选择的累积能得到全局最优解// 问题抽象化表示 vectorint trains {8,4,2,5,3,9,1,6,7}; // 输入序列 int minTracks solve(trains); // 求解最少轨道数2. STL set的魔法自动维护有序集合STL set基于红黑树实现具有以下关键特性自动排序元素按升序排列快速查找O(log n)时间复杂度的查找操作唯一性保证集合中不存在重复元素操作时间复杂度说明insertO(log n)插入元素并维护有序性lower_boundO(log n)二分查找第一个≥x的元素eraseO(log n)删除指定元素提示set的lower_bound比vector的lower_bound更高效因为set底层是平衡二叉搜索树而vector需要先排序才能使用二分查找。3. 贪心策略的具体实现我们的算法核心思想是为每列火车寻找最小可插入轨道。具体步骤初始化一个空set来记录各轨道末尾数字对于每列火车x在set中查找第一个≥x的元素如果找到替换该元素为x如果未找到将x作为新轨道加入setset的最终大小即为所需最少轨道数#include iostream #include set using namespace std; int main() { int n, x; setint tracks; cin n; for(int i0; in; i) { cin x; auto it tracks.lower_bound(x); if(it ! tracks.end()) { tracks.erase(it); } tracks.insert(x); } cout tracks.size(); return 0; }这个实现巧妙利用了set的特性lower_bound快速定位插入位置自动排序保证总能找到最优轨道**O(n log n)**的整体时间复杂度完美处理1e5量级数据4. 为什么贪心策略有效理解算法正确性的关键在于三点轨道末尾数字越小接纳能力越强轨道末尾数字决定了能接收哪些后续列车保留较大的末尾数字可以为后续更大的列车留出空间替换策略不会增加轨道数量替换操作相当于将列车插入现有轨道只有在无法插入时才新增轨道set大小即为LIS长度这个问题等价于求序列的最长上升子序列长度每个轨道对应一个上升子序列中的元素5. 实战技巧与性能对比在实际竞赛中选择合适的数据结构至关重要。我们对比set和vector两种实现特性STL setSTL vector插入效率O(log n)O(n)查找效率O(log n)O(log n)内存使用较高较低代码简洁性更简洁稍复杂适用数据量大(1e5)小(1e4)注意PTA竞赛环境不支持bits/stdc.h万能头文件需显式包含或对于时间敏感的竞赛题set通常是更好的选择。它不仅代码更简洁而且在大数据量时性能更稳定。下面是用vector实现的版本供对比#include iostream #include vector #include algorithm using namespace std; int main() { int n, x; vectorint tails; cin n; for(int i0; in; i) { cin x; auto it lower_bound(tails.begin(), tails.end(), x); if(it tails.end()) { tails.push_back(x); } else { *it x; } } cout tails.size(); return 0; }6. 常见错误与调试技巧在实现这个算法时容易遇到以下几个陷阱忽略输入顺序必须按照题目给定的顺序处理列车不能先排序再处理错误理解lower_bound它返回的是第一个≥x的元素如果需要严格大于应使用upper_boundset迭代器失效在erase后被删除的迭代器会失效不能继续使用已删除的迭代器调试时可以添加打印语句观察set的变化for(auto num : tracks) { cout num ; } cout endl;7. 算法扩展与应用这个技巧不仅适用于列车调度问题还能解决许多类似问题俄罗斯套娃信封问题给定信封的宽高求最多能套多少层解法先排序然后使用类似的贪心策略会议室安排II给定若干会议的开始结束时间求最少需要多少会议室航班调度系统安排飞机在最少登机口停靠每个登机口只能同时服务一架飞机掌握这种贪心思想你就能举一反三解决许多实际问题。在竞赛中遇到类似题目时不妨思考能否抽象为序列覆盖问题能否使用有序数据结构优化查找贪心策略是否满足最优子结构