BZOJ2080 POI2010 Railway & NOIP2008 双栈排序


POI2010 题解整理

题目大意

给定序列( N105 ),判断能否进行双栈排序。

Sample Input

4
1 3 4 2

4
2 3 4 1

Sample Output

TAK
1 1 2 1

NIE

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栈

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值