您的位置:首页 > 理论基础 > 数据结构算法

【栈】华为OJ:火车进站

2016-07-25 09:41 399 查看

华为OJ题目:火车进站

题目描述:给定一个正整数N代表火车数量,0<N<10,接下来输入火车入站的序列,一共N辆火车,每辆火车以数字1-9编号。要求以字典序排序输出火车出站的序列号。

例如输入:3 1 2 3 
输出例子:1 2 31 3 22 1 32 3 13 2 1
格式要求:输出每组换行,每组数字间空格隔开,数字末尾不能有空格

分析:
这里题目的描述不是很清楚,火车站应视为一个栈,有着后进先出的特性,比如进站顺序为1 2 3。根据进出站时间的不同,可以有很多种情况。但不是1 2 3的每种排列都可以。
1.假如1 2 3依次进站,出站只能为3 2 1;
2.假如1进站,又出站,此时2 3再依次进站,那么出栈顺序只能是1 3 2。
3.假如要想3先出站,那么1,2必须先进站且不出站,那么gen'j2就必然比1先出站,就不可能出现3 1 2的出栈顺序。

可以用next_permutation()对进站序列进行全排列,然后对所有的排列进行排除。next_permutation求一个排序的下一个排列的函数,包含在头文件#include<algorithm>中,其参数为所求排列的首尾地址。

方法1:栈方法。
创建两个序列,序列1为火车进站序列,即题目的输入序列;序列2对序列1进行从小到大的排序,然后反复对序列2使用next_permutation,就可以获取序列2的字典序全排列(也就是题目要求的输出顺序),这样只要从这个全排列中剔除不符合要求的排列即可。剔除方法是:首先让第一列火车入栈,对于列举的每种排列的每个数,将其与栈顶比较。如果不相等就让下一列火车入栈,如果相等就删除栈顶(即当前火车出站),比较排列的下一个数,如果栈为空就将下一列火车入栈,以此类推。如果所有的数都可以找到与之对应的栈顶,则该排列符合要求,如果直到最后一辆火车都已经入栈,也找不到与排列数相等的栈顶,则该排列是不符合条件的。
例子:输入火车序列为6 1 5 3 2 7 4,对其进行排序1 2 3 4 5 6 7,再反复使用next_permutation,得到1 2 3 4 5 6 7到7 6 5 4 3 2 1的全部的排列。对每种排列判断它是否符合出栈的要求。例如对于第一个排列1 2 3 5 6 7,
(a)先比较1,先让火车6入栈,与1比较不相等,火车1入栈,与1相等,删除栈顶(stack:6 1,删除后stack:6)
(b)再比较2,与栈顶不相等,依次将5 3入栈,都不相等,将2入栈,相等,删除栈顶(stack:6 5 3 2,删除后stack:6 5 3)

(c)再比较3,与栈顶相等,删除栈顶3(stack:6 5 )
(d) 再比较4,与栈顶不等,火车7入栈,不等,火车4入栈,相等,删除栈顶(stack:6 5 7 4,删除后stack:6 5 7)
(e) 再比较5,与栈顶不等。
已经没有火车可以进栈了,因此这种排列是不行的。若排列改成1 2 3 4 7 6 5,则火车可以顺利进出栈,则符合要求

注意:
1.只有当当前比较的数和栈顶相等时才能删除栈顶。
2.这里使用do…while的原因是,要先处理第一个排列之后(例如1 2 3 4 5 6 7),才能利用next_permutation处理下一个,所以do{}的内容需要提前执行。

完整代码如下:

#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;
bool IsRightSq(int m_temp[],int m_train[],int Num);//判断当前排列是否符合条件
int main()
{
int N;//火车数量
cin>>N;
int train[9]={0};//火车进号码
int temp[9]={0};//拷贝,并进行全排列
int i;
for(i=0;i<N;i++)
{
cin>>train[i];
temp[i]=train[i];
}
sort(temp,temp+N);//对temp排序,以便从头开始全排列
do
{
if(IsRightSq(temp,train,N))//如果当前排列符合,则输出
{
for(int k=0;k<N-1;k++)
cout<<temp[k]<<" ";
cout<<temp[N-1]<<endl;//注意OJ上的格式要求:最后一个数字后不加空格
}
}
while(next_permutation(temp,temp+N));//每次获取下一个排列
return 0;
}
bool IsRightSq(int m_temp[],int m_train[],int Num)//判断当前排列是否符合条件
{
stack<int> s;
int n=0,k;
while(!s.empty())//清空栈
s.pop();
for(k=0;k<Num;k++)//对当前排列的每一个值
{
while(n!=Num+1)
{
if(s.empty()||s.top()!=m_temp[k])//取栈顶比较(栈为空则将下一列火车入栈)
{
s.push(m_train
);//把下一列火车压入栈内
n++;
}
else//如果栈顶等于当前值,说明火车该出栈。
{
s.pop();
break;
}
}
}
if(s.empty())//当循环遍历完,符合条件的排列,火车应该全部出栈,栈为空
return 1;
else
return 0;
}

方法2:利用火车出站数的特征
和方法1一样列出所有的全排列,然后进行剔除,剔除方法:根据后进先出的特性,对每一个出栈的火车,其后所有顺序号比它小的火车,都必须保持顺序号递减的关系。据此,可以将不符合条件的数组从全排列中剔除。
假设数组train为火车进站序列,例如3 7 2 4 5 1 6,用另一个数组temp记录它们的顺序关系,即6 3 1 4 5 7 2,(如火车号为2的火车是第3个进站的temp[2-1]=3,火车号为7的火车是第2个进站的,temp[7-1]=2)。对3
7 2 4 5 1 6排序后得到1 2 3 4 5 6 7,并反复利用next_permutation得到它的字典序全排列。
(a)假设对其中一个排列4 5 1 2 7 3 6,
对第一个数4,其顺序号为temp[4-1]=4,其后顺序号比它小的有火车2,7,3,其顺序号分别为:temp[2-1]=3、temp[7-1]=2、temp[3-1]=1,是依次递减的,符合要求。
对第二个数5,其后火车2 7 3顺序号比它小,且依次递减,符合要求。
对第三个数1,其后火车271顺序号比它小,且依次递减,符合要求。
对第四个数2,其后火车7,3顺序号比它小,且依次递减,符合要求。
对第五个数7,其后火车2的顺序号比它小
对第六个数3,它是第一个进站的,没有火车顺序号比它小
对第七个数6,其后没有火车。
因此全部符合要求,这个排列是符合条件的。
(b)假设对另一个全排列7 3 6 5 1 4 2
对第一个数7,其后火车3的顺序号比它小
对第二个数3,它是第一个进站的,没有火车顺序号比它小
对第三个数6,其后火车5 1 4 2顺序号都比它小,分别为temp[5-1]=5、Temp[1-1]=6、temp[4-1]=4、temp[2-1]=3,不是递减的,因此不符合要求

完整代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int N;//火车数量
cin>>N;
int train[9]={0};//火车进号码
int temp[10]={0};//进站先后顺序
int i,j;
for(i=0;i<N;i++)
{
cin>>train[i];//train记录进站火车号,需要对它字典序输出
temp[train[i]]=i;//temp记录进站先后顺序,需要对它进行比较(是否符合出栈要求)
}
sort(train,train+N);
do
{
int flag=1;//该排列不符合,则置零
for(i=0;i<N-1;i++)//对于每一个出栈数temp[train[i]],(接下)
{
int current=temp[train[i]];
int cmp=current;
for(j=i;j<N;j++)//它之后所有比它小的数
{
if(temp[train[j]]<current && temp[train[j]]<=cmp)//必须保持顺序号递减
cmp=temp[train[j]];
else if(temp[train[j]]<current && temp[train[j]]>cmp)//否则这个排序不符合
flag=0;//此排序不符合,flag标记为0
else
continue;
}
}
if(flag==0)
continue;
else
for(int k=0;k<N-1;k++)
cout<<train[k]<<" ";
cout<<train[N-1]<<endl;
}
while(next_permutation(train,train+N));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 数据结构 华为