火车站的列车调度铁轨的结构如下图所示。
两端分别是一条入口(Entrance)轨道和一条出口(Exit)轨道,它们之间有N
条平行的轨道。每趟列车从入口可以选择任意一条轨道进入,最后从出口离开。在图中有9趟列车,在入口处按照{8,4,2,5,3,9,1,6,7}的顺序排队等待进入。如果要求它们必须按序号递减的顺序从出口离开,则至少需要多少条平行铁轨用于调度?
输入格式:
输入第一行给出一个整数N
(2 ≤ N
≤105),下一行给出从1到N
的整数序号的一个重排列。数字间以空格分隔。
输出格式:
在一行中输出可以将输入的列车按序号递减的顺序调离所需要的最少的铁轨条数。
输入样例:
9
8 4 2 5 3 9 1 6 7
输出样例:
4
我们先来分析一下题目,我们可以知道,左侧进来的列车号码是随机的,而这些列车从右侧离开时,我们却要保证他从大到小依次离开。
一眼看上去很复杂,所以我们可以思考一下将他简化。
左侧到右侧之间,有我们为这些列车设计的调度铁轨,每条铁轨可以停上任意辆车,同时我们也可以将从左到右的过程来分为两步:
1.列车由左进入调度铁轨。
2.列车由调度铁轨从右离开。
我们不难发现,因为最后要求我们让列车按序号从大到小离开,我们必须要让列车在调度轨道上是从小到大排序,不然会出现“小压大”的情况,被“压住”的大序号列车永远无法出去。如下:
所以我们必须确保从左进入调度轨道时,调度轨道上的列车是从小到大排列的。
多条存有序号升序列车的轨道平行排列后,我们就可以保证最后从右离开的列车是从大到小依次离开的,只需要每次都选取所有调度轨道上列车序号最大的一个即可。(然而这个问题不在我们的讨论范围之内,我们只需证明需要多少个调度轨道)。
所以我们的问题再一次简化。
我们只需考虑第一步:列车由左进入调度铁轨,且在所有调度轨道上,列车的序号是由小到大排列的。
我们来模拟一下:如果一辆列车从左进入调度轨道,他有两个选择:
1.进入已经有列车的轨道,排在他的后面。
2.开辟一个新轨道。
很显然,判断的依据就是,我们这俩车的序号,是否大于所有已经有车的轨道上的最后一个列车(同时也是这一轨道上序号最小的列车)的序号,如果存在尾车比我们的序号大,我们可以理所当然的跟在他后面,而如果所有尾车都比我们序号小,在任何一个轨道都会存在小压大的情况,那我们只能开辟一个新轨道。
值得一提的是,如果有两个以上的尾车序号都比我们大,我们需要跟在他们之中序号最小的一个后面,举个例子,如我们的车是6,两个尾车有7和9,我们需要跟在7后面,这是因为我们需要为可能出现的8做准备,如果6在9后面,那么8出现的时候我们就要新开一个轨道(本来可能没必要开),这有可能使我们最后的答案有误。
那么如何找到这个尾车的序号呢
相信聪明的朋友们从刚刚说到顺序问题时,就想到了--set
并且,在上一篇介绍set的文章(STL容器(2)-set_hanbaor的博客-CSDN博客)中,我还介绍了两个函数:
lower_bound(key_value) ,返回第一个大于等于key_value的定位器。如果不存在,返回end();
upper_bound(key_value),返回第一个大于key_value的定位器。如果不存在,返回end();
题目中每个序号都不同,两个函数在此代码中作用相同,我使用了前者。
好了,到这里让我们来上代码吧。
#include <iostream>
#include <set>
using namespace std;
int main()
{
int n,m;
cin>>n;
set<int> st;
for(int i=0;i<n;i++)
{
cin>>m;
if(st.lower_bound(m)!=st.end())//判断是否存在尾车序号大于正在进入的车
{
st.erase(st.lower_bound(m));//若存在,先让原尾车离开
}
st.insert(m); //无论尾车是否存在,新列车都需要进入轨道
//这里其实很好理解,如果能够跟在尾车后面,原尾车出去,新列车进,set的size相当于没变。
//而符合条件的尾车不存在,直接让新车进入轨道,这时候set的size会加1,意为开了一个新轨道
}
cout<<st.size()<<endl;
return 0;
}
代码是不是出乎意料的短?
让我们来细细品味一下,感受这短短几行的奇妙之处。
以样例为例,前三个数8,4,2正好是一个完美的序列。
8:第一个数,直接开辟一条新轨道进入。
4:通过lower_bound(4),可以得到8,所以进入if语句,用让erase让8离开,同时,insert(4),这里是这个语句的第一个含义:让该元素跟车进入
2:与4同理,不再赘述。
5:出现了特殊情况,这时候,我们的set容器中只有2,很显然2<5,所以我们不能进入if语句,直接insert(5),这里就是这个语句的第二个含义:当不能跟车进入的时候,开辟新轨道
这也就是我在代码注释里写到的,无论时跟车还是开辟新轨道,这个新车都要进入调度,所以insert语句必须执行。
3:此时set中有2和5,3只能选择顶掉5,
9:最大的数字,只能开辟新轨道。
1:最小的数字,特殊情况再次出现,此时set中有2,3,9三个数字,每个数字都比1大,还记得
lower_bound(key_value) 的作用吗,他返回的是第一个大于等于key_value的定位器。所以这时候,我们的1很巧妙的顶掉了2,
6:此时set中为1,3,9三个数字,6只能顶掉9,
7:此时set中为1,3,6三个数字,7开辟新轨道,
最终,我们的set容器中为1,3,6,7.
其size()为4,
得解
pta得分点全过:
完