luoguP1155 双栈排序

来源:互联网 发布:linux卸载snmp服务 编辑:程序博客网 时间:2024/06/03 10:38

题目描述

Tom最近在研究一个有趣的排序问题。如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序。

操作a
如果输入序列不为空,将第一个元素压入栈S1
操作b
如果栈S1不为空,将S1栈顶元素弹出至输出序列
操作c
如果输入序列不为空,将第一个元素压入栈S2
操作d
如果栈S2不为空,将S2栈顶元素弹出至输出序列

输入输出格式

输入格式:
输入文件twostack.in的第一行是一个整数n。
第二行有n个用空格隔开的正整数,构成一个1~n的排列。

输出格式:
输出文件twostack.out共一行,如果输入的排列不是“可双栈排序排列”,输出数字0;否则输出字典序最小的操作序列,每两个操作之间用空格隔开,行尾没有空格。

输入输出样例

输入样例#1:
【输入样例1】
4
1 3 2 4

【输入样例2】
4
2 3 4 1

【输入样例3】
3
2 3 1

输出样例#1:
【输出样例1】
a b a a b b a b

【输出样例2】
0

【输出样例3】
a c a b b d

说明
30%的数据满足: n<=10
50%的数据满足: n<=50
100%的数据满足: n<=1000

分析:
第一眼看过去,很简单啊,模拟一下就好了
只要遵循:
在栈里的元素一定是单调递减的
栈里相邻元素相差的越少越好,
优先选择第一个栈
实测:40分
这完全就是钻制度的空子。。。

好吧,说正经的

考虑a[i],a[j]两个元素不能进入同一个栈的条件.
注意,这里所说的”a[i],a[j]两个元素不能进入同一个栈“,
是自始至终不能进入一个栈,
即如果有解,那么a[i],a[j]一定进入过的栈不同.

条件:存在k使得i < j < k且a[k] < a[i] < a[j]
进栈顺序i->j->k
栈中的元素一定是单调减的
如果ta们在一个栈中,那么进栈顺序是j->i->k,与原命题矛盾

那知道了这个有什么用呢
我们可以知道元素两两能不能在一起,
如果i和j不能在一起,j和k不能在一起,i和k不能在一起,那原序列无解

我们把不能在一起的一对点之间连边
(突然觉得这蜜汁像关押罪犯,当有冲突时,就把边两端的点分配到不同的集合中去)
之后遍历一遍进行01染色
这样我们知道了进栈的状况,就可以直接模拟输出了
(直接上40分的骗分)

tip

按理说在处理冲突点对的时候是O(n^3)的复杂度,对于1000太大了,大神说要用dp优化一下,但是luogu数据实在是太耿直了,打的三方暴力就这样A了

简单说一下dp优化,
因为我们只要找到一个k满足就判定是冲突点对,
所以设f[i]表示i-n中的最小值
如果最小值都满足不了那就不可能了
这样可以把k的循环降到O(1)

这里写代码片#include<cstdio>#include<cstring>#include<iostream>#include<queue>using namespace std;const int N=1010;int n,a[N];char s[N];struct node{    int x,y,nxt;};node way[N<<1];int st[N],tot=0,co[N],st1[N],st2[N];void add(int u,int w){    tot++;    way[tot].x=u;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;    tot++;    way[tot].x=w;way[tot].y=u;way[tot].nxt=st[w];st[w]=tot;}void doit(){    int i,j;    memset(co,-1,sizeof(co));    queue<int> q;    q.push(way[1].x);    co[way[1].x]=0;    while (!q.empty())   //染色     {        int r=q.front();        q.pop();        for (i=st[r];i;i=way[i].nxt)        {            if (co[way[i].y]==-1)             {                co[way[i].y]=(co[r]==0 ? 1:0);                q.push(way[i].y);            }            else if (co[way[i].y]==co[r])            {                printf("0\n");                return;            }        }    }    int t=-1,t1=0,t2=0,k=1;    for (i=1;i<=n;i++)    {        if (a[k]==i)        {            printf("a b ");k++;            continue;        }        if (st1[t1]==i)        {            printf("b "); t1--; continue;        }        if (st2[t2]==i)        {            printf("d "); t2--; continue;        }        while (a[k]!=i)        {            if (co[k]==0||co[k]==-1)               printf("a "),st1[++t1]=a[k];            else printf("c "),st2[++t2]=a[k];            k++;        }        printf("a b ");k++;    }    return; }int main(){    scanf("%d",&n);    for (int i=1;i<=n;i++)        scanf("%d",&a[i]);    for (int i=1;i<n-1;i++)  //i<j<k  a[k]>a[i]>a[j]        for (int j=i+1;j<n;j++)        {                       if (a[i]<a[j])            for (int k=j+1;k<=n;k++)                if (a[k]<a[i])                {                    add(i,j);break;                }         }    doit();    return 0;}