【NOIP2008】双栈排序

Description

Tom最近在研究一个有趣的排序问题。如图所示,通过2个栈S1和s2,Tom希望借助以下4种操作实现将输入序列升序排序。
1146405-20171109164822263-36774730.jpg
1、操作a
如果输入序列不为空,将第一个元素压入栈S1
2、操作b
如果栈S1不为空,将S1栈顶元素弹出至输出序列
3、操作c
如果输入序列不为空,将第一个元素压入栈s2
4、操作d
如果栈S2不为空,将S2栈顶元素弹出至输出序列
如果一个1~n的排列P可以通过一系列操作使得输出序列为l,2,…,(n-1),n,Tom就称P是一个“可双栈排序排列”。例如 (1,3,2,4)就是一个“可双栈排序排列”,而(2,3,4,1)不是。下图描述了一个将(1,3,2,4)排序的操作序 列:
1146405-20171109164838153-1970173044.jpg
当然,这样的操作序列有可能有多个,对于上例(1,3,2,4),是另外一个可行的操作序列。Tom希望知道其中字典序最小的操作序列是什么。

solution

好难的题啊.
分析一个栈的情况:只能是一个或多个递减连续序列组成的,如 \(3,2,1,6,5,4,9,8,7\),如果 \(3,2,1\) 之间出现了 \(3,5,1\) 的情况,就会存在问题,严格的讲,就是满足 \(i<j,a[i]<a[j]\),且 \(j\) 后面存在比 \(i\) 小的数 这样的二元组 \((i,j)\),但是双栈排序就可以很好的解决,我们需要把每一个元素分配给两个栈,然后模拟单栈排序的过程即可,至于分配的条件:如果我们把 \((i,j)\) 连边,那么有连边的不能二元组在同一个集合中,这就是二分图的定义,我们二分图染色一下即可,字典序的问题只要我们把前面的元素尽量分给第一个栈就可以满足最小了

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=1005;
int n,a[N],dp[N],num=0,head[N],to[N*N*2],nxt[N*N*2],col[N];

void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}

inline void dfs(int x){
    for(int i=head[x];i;i=nxt[i]){
        int u=to[i];
        if(col[u]){
            if(col[u]+col[x]!=3){puts("0");exit(0);}
            continue;
        }
        col[u]=3-col[x];
        dfs(u);
    }
}

int now=1,st[N],skt[N],top=0,sktop=0;
inline void P(char c){putchar(c);putchar(' ');}
inline void solve(int x){
    if(col[x]==1)st[++top]=a[x],P('a');
    else skt[++sktop]=a[x],P('c');
    while((top && st[top]==now) || (sktop && skt[sktop]==now)){
        if(st[top]==now)top--,P('b');
        else if(skt[sktop]==now)sktop--,P('d');
        now++;
    }
}

void work()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    dp[n]=a[n];dp[n+1]=N;
    for(int i=n-1;i>=1;i--)dp[i]=Min(dp[i+1],a[i]);
    
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            if(a[i]<a[j] && a[i]>dp[j])
                link(i,j),link(j,i);
        }
    }
    for(int i=1;i<=n;i++)
        if(!col[i]){
            col[i]=1;
            dfs(i);
        }   
    for(int i=1;i<=n;i++)solve(i);
}

int main()
{
    work();
    return 0;
}

转载于:https://www.cnblogs.com/Yuzao/p/7810293.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值