题目大意
给定序列( N≤105 ),判断能否进行双栈排序。
Sample Input
4
1 3 4 24
2 3 4 1Sample Output
TAK
1 1 2 1NIE
Output Details
TAK is equal to YES, and NIE is equal to NO.
样例1:火车1进入侧线1->火车1出来->火车2进入侧线->火车3进入侧线2->火车2进入侧线1->火车2出来->火车3出来->火车4出来。
主要还是Spy大神和chk大神分别写了这题的两种解法,我一个小蒟蒻还是讲讲自己的心得吧。
写到这题直接想到了NOIP2008双栈排序的那题,那道题目在判断是否可以双栈排序的时候采用的是暴力建边以及二分图染色,时间复杂度是 O(n2) ,不足以解决本题。
#define M 1005
int n,q[M],col[M];
vector<int>G[M];
void Add_edge(int u,int v){
G[u].push_back(v);
G[v].push_back(u);
}
bool flag=1;
void color(int u){
for(int j=0;j<G[u].size();j++){
int v=G[u][j];
if(col[v]==-1){col[v]=col[u]^1;color(v);}
else if(col[v]==col[u]){flag=0;return;}
}
}
……
for(int i=1;i<=n;i++){
int edge=-1;//q[i]不构成二分图树林,直接塞进S1中
for(int k=n;k>=i;k--)
if(q[k]<q[i]){edge=k;break;}
if(edge==-1){col[i]=0;continue;}
for(int j=i+1;j<=edge;j++)
if(q[i]<q[j])Add_edge(i,j);
}
for(int i=1;i<=n;i++)
if(col[i]==-1){
col[i]=0;color(i);
if(!flag){puts("0");return 0;}
}//二分图染色
……
上述构图的操作基于以下这个表达式:
- 对于该序列中的一组 i,j,k(i<j<k) ,如果存在 q[k]<q[i]<q[j] ,那么 i,j 一定不能同时存在于一个栈中(不只是当前同时存在,也包括曾被压入同一个栈的情况)。
通过举例亦可以非常快速的得到结果,严谨证明在codevs上有大神写过。如果满足上述条件,首先i要比j,k都先压入栈中,而且为了使i弹出,k必须要先弹出,但是j已经压住了i,所以无法进行双栈排序。
之后由于将点分成两个不同点集并且处于不同点集的点之间才有连线,这个新图的构成才让我们想到用二分图进行处理。
由于考虑字典序,所以操作的时候要优先执行a,b的操作。即在有a,d同时出现的情况下,先a再d;在有b,c同时出现的情况下,先b再c。
(其实打印解也是一个比较困难的问题,但是请考虑自己实现)
以上是双栈排序的题解,接下来我们考虑如何改进 O(n2) 的算法。
导致复杂度过高的原因是我们进行了许多无用的建边。对于最后排序好的序列,可以设想一定会有连续一段区间内的元素都是从1栈出来或者是从2栈