PTA 天梯赛 L2-014:列车调度 ← STL set / STL vector

【题目来源】
https://pintia.cn/problem-sets/994805046380707840/exam/problems/type/7?page=1&problemSetProblemId=994805063166312448

【问题描述】
火车站的列车调度铁轨的结构如下图所示。

L2-014

两端分别是一条入口(Entrance)轨道和一条出口(Exit)轨道,它们之间有 N 条平行的轨道。每趟列车从入口可以选择任意一条轨道进入,最后从出口离开。在图中有 9 趟列车,在入口处按照{8,4,2,5,3,9,1,6,7} 的顺序排队等待进入。如果要求它们必须按序号递减的顺序从出口离开,则至少需要多少条平行铁轨用于调度?

【输入格式】
输入第一行给出一个整数 N (2 ≤ N ≤10^​5​​ ),下一行给出从 1 到 N 的整数序号的一个重排列。数字间以空格分隔。

【输出格式】
在一行中输出可以将输入的列车按序号递减的顺序调离所需要的最少的铁轨条数。

【输入样例】
9
8 4 2 5 3 9 1 6 7

【输出样例】
4

【样例说明】
若将若干列车放在一起,则它们的列车序号便构成一个序列。若每条铁轨上的列车序号构成的递减序列尽可能长,则便可得到最少的铁轨条数。当然,虽然求得的最少铁轨条数一样,但是每条铁轨上的列车序号递减序列可能并不唯一。其中,关于本题的一种列车序号递减序列如下所示:
第一条:2 4 8
第二条:1 3 5
第三条:6 9
第四条:7

【算法分析】
● STL set 中的元素自构“递增无重”。STL set 中的迭代器可理解为“指针”。

● STL set 常用函数详见:https://blog.csdn.net/hnjzsyjyj/article/details/145528031
(1)lower_bound(x):返回一个迭代器,该迭代器指向第一个大于等于 x 的元素。若无,返回 end() 得到的迭代器。
(2)upper_bound(x):返回一个迭代器,该迭代器指向第一个大于 x 的元素。若无,返回 end() 得到的迭代器。
(3)erase(first, last):从集合中删除迭代器 first 及迭代器 last 指向的区间 [first, last) 中的元素(注意:左闭右开)。
(4)begin():返回一个迭代器,该迭代器指向集合第一个元素的位置。
(5)end():返回一个迭代器,该迭代器指向集合最后一个元素的下一个位置。

#include <bits/stdc++.h>
using namespace std;
 
set<int> iset;
set<int>::iterator itlow,itup;
int n,x;
 
int main () {
    cin>>n;
    for(int i=1; i<=n; i++) {
        cin>>x;
        iset.insert(x);
    }
 
    for(set<int>::iterator it=iset.begin(); it!=iset.end(); it++) {
        cout<<*it<<" ";  //3 5 6 7 8 12 38 66 90
    }
 
    itlow=iset.lower_bound(6); //greater than or equal to 6, point to 6
    itup=iset.upper_bound(12); //greater than 12, point to 38
    iset.erase(itlow,itup); //erase elements in [6,38)
 
    cout<<endl;
    for(set<int>::iterator it=iset.begin(); it!=iset.end(); it++) {
        cout<<*it<<" ";  //3 5 38 66 90
    }
 
    return 0;
}
 
/*
in:
9
12 7 3 38 5 6 90 8 66

out:
3 5 6 7 8 12 38 66 90
3 5 38 66 90
*/

● PTA 天梯赛不支持万能头文件。

● 关键函数作用
(1)it = t.lower_bound(x)
在有序集合中二分查找,返回指向第一个大于等于 x 的元素的迭代器,这是贪心策略结合二分优化的核心。
(2)if (it != t.end())
若找到满足条件的元素,说明存在可替换的序列结尾,将其删除并替换为 x。这体现了贪心思想,即用更小的数值作为序列结尾,能为后续元素留出更大的上升空间,使整体序列更容易延长
(3)t.size()
最终集合的大小,即为原序列的最长上升子序列(LIS)长度。

● 因为分配方式可以自由组合,每个数字具体放入哪条通道并不唯一,但最少需要的通道数一定是唯一的。我们其实不需要关心每条通道内部长什么样,只需要记录每条通道的最后一个数字,就可以完成整个贪心过程。本题中,STL set 的作用就是存储所有通道当前的最后一位数字,并且自动保持有序、不重复。

贪心思想:由于每条轨道内的编号必须单调递减,只有比轨道末尾数字更小的列车才能进入该轨道。因此,轨道末尾数字越大,后续能接纳的数字范围就越广,调度潜力也就越强。基于这一特点,我们采用如下贪心策略:对于每一辆待调度的列车,在所有可以接纳它的轨道中,选择末尾数字最小的那条轨道放入。这样可以尽可能保留那些末尾数字更大、调度潜力更强的轨道,为后续更大的数字留出充足的安排空间,从而最大程度复用已有轨道,减少所需轨道的总数。

【算法代码一:STL set】

#include <iostream>
#include <set>
using namespace std;

set<int> t;
set<int>::iterator it;

int main() {
    int n,x;
    cin>>n;
    for(int i=1; i<=n; i++) {
        cin>>x;
        it=t.lower_bound(x); //返回一个迭代器,指向第一个大于等于x的元素
        if(it!=t.end()) {
            t.erase(it); //删除迭代器it指向的元素
            t.insert(x);
        } else t.insert(x);
    }
    cout<<t.size();
    return 0;
}


/*
in:
9
8 4 2 5 3 9 1 6 7

out:
4
*/

【算法代码二:STL vector

#include <bits/stdc++.h>
using namespace std;

vector<int> v;
vector<int> t; //tails
int n,x;

int main() {
    cin>>n;
    while(n--) {
        cin>>x;
        v.push_back(x);
    }

    for(int x:v) {
        auto it=lower_bound(t.begin(),t.end(),x);
        if(it==t.end()) t.push_back(x);
        else *it=x;
    }
    cout<<t.size()<<endl;

    return 0;
}

/*
in:
9
8 4 2 5 3 9 1 6 7

out:
4
*/






【参考文献】
https://blog.csdn.net/hnjzsyjyj/article/details/127017796
https://blog.csdn.net/Captain0827/article/details/105138858
https://blog.csdn.net/SinclairWang/article/details/104025277
https://cplusplus.com/reference/set/

 

posted @ 2026-03-24 22:44  Triwa  阅读(36)  评论(0)    收藏  举报