洛谷 1155 (NOIp2008)双栈排序——仔细分析不合法的条件

题目:https://www.luogu.org/problemnew/show/P1155

这道题教会我们要多思考。

好好分析过后发现同一个栈里不能有升序。就用它写了一个30分。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005;
int n,stack[2][N],top[2],nw,tot,ps[N];
char ch[N<<1];
bool flag;
int main()
{
    scanf("%d",&n);int x;nw=1;
    stack[0][0]=stack[1][0]=n+1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        if(stack[0][top[0]]>x){
            stack[0][++top[0]]=x;
            ch[++tot]='a';ps[x]=1;
        }
        else if(stack[1][top[1]]>x){
            stack[1][++top[1]]=x;
            ch[++tot]='c';ps[x]=2;
        }
        else {
            flag=1;break;
        }
        while(ps[nw])
        {
            if(ps[nw]==1){
                top[0]--;ch[++tot]='b';
            }
            else{
                top[1]--;ch[++tot]='d';
            }
            nw++;
        }
    }
    if(flag)printf("0");
    else for(int i=1;i<=tot;i++)printf("%c ",ch[i]);
    return 0;
}
View Code

然后发现只有30分。想想自己想得太简单了。于是写了个深搜30分。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005;
int n,stack[2][N],top[2],tot,ps[N],a[N];
char ch[N<<1];
bool flag;
void rd(int x,int k)
{
    stack[k][++top[k]]=x;ch[++tot]=(k?'c':'a');ps[x]=k+1;
//    printf("stack%d=%d(top[k]=%d)\n",k,stack[k][top[k]],top[k]);
}
void clr(int x,int k)
{
    top[k]--;tot--;ps[x]=0;
}
void cd(int &nw)
{
//    printf("cd!\n");
    while(ps[nw])
    {
        if(ps[nw]==1){
            top[0]--;ch[++tot]='b';
        }
        else{
            top[1]--;ch[++tot]='d';
        }
        nw++;
    }
}
void dfs(int cr,int nw)
{
    if(cr>n)return;
    int x=a[cr],tnw=nw;bool cflag=0;
    int tsk[2][N];memcpy(tsk,stack,sizeof stack);
    if(stack[0][top[0]]>x&&stack[1][top[1]]<x)
    {
        rd(x,0);
        if(ps[nw])cflag=1,cd(nw);
//        printf("cr=%d 0\n",cr);
        dfs(cr+1,nw);
        if(flag){
            if(cflag){
                tot-=nw-tnw;top[0]+=nw-tnw;nw=tnw;
                memcpy(stack,tsk,sizeof tsk);
            }
            clr(x,0);
//            printf("ret cr=%d flag=1\n",cr);
        }
    }
    else if(stack[1][top[1]]>x&&stack[0][top[0]]<x)
    {
        rd(x,1);
        if(ps[nw])cflag=1,cd(nw);
//        printf("cr=%d 1\n",cr);
        dfs(cr+1,nw);
        if(flag){
            if(cflag){
                tot-=nw-tnw;top[1]+=nw-tnw;nw=tnw;
                memcpy(stack,tsk,sizeof tsk);
            }
            clr(x,1);
//            printf("ret cr=%d flag=1\n",cr);
        }
    }
    else if(stack[1][top[1]]<x&&stack[0][top[0]]<x)
    {
//        printf("cr=%d flag=1!\n",cr);
        flag=1;return;
    }
    else{
//        printf("stack0=%d stack1=%d(top0=%d top1=%d)\n",stack[0][top[0]],stack[1][top[1]],top[0],top[1]);
        rd(x,0);
        if(ps[nw])cflag=1,cd(nw);
//        printf("cr=%d try0\n",cr);
        dfs(cr+1,nw);
        if(flag){
            if(cflag){
                tot-=nw-tnw;top[0]+=nw-tnw;nw=tnw;
                memcpy(stack,tsk,sizeof tsk);
            }
            clr(x,0);
//            printf("cr=%d failtry0 try1\n",cr);
            flag=0;cflag=0;rd(x,1);
            if(ps[nw])cflag=1,cd(nw);
            dfs(cr+1,nw);
            if(flag){
                if(cflag){
                    tot-=nw-tnw;top[1]+=nw-tnw;nw=tnw;
                    memcpy(stack,tsk,sizeof tsk);
                }
                clr(x,1);
//                printf("cr=%d failtry1\n",cr);
            }
        }
    }
}
int main()
{
    scanf("%d",&n);int x;
    stack[0][0]=stack[1][0]=n+1;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    dfs(1,1);
    if(flag)printf("0");
    else for(int i=1;i<=tot;i++)printf("%c ",ch[i]);
    return 0;
}
View Code

然后发现只有30分。那就剪剪枝吧。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005;
int n,stack[2][N],top[2],tot,ps[N],a[N];
char ch[N<<1];
bool flag;
void rd(int x,int k)
{
    stack[k][++top[k]]=x;ch[++tot]=(k?'c':'a');ps[x]=k+1;
//    printf("stack%d=%d(top[k]=%d)\n",k,stack[k][top[k]],top[k]);
}
void clr(int x,int k)
{
    top[k]--;tot--;ps[x]=0;
}
void cd(int &nw)
{
//    printf("cd!\n");
    while(ps[nw])
    {
        if(ps[nw]==1){
            top[0]--;ch[++tot]='b';
        }
        else{
            top[1]--;ch[++tot]='d';
        }
        nw++;
    }
}
bool check(int cr,int x)
{
    int i;
    for(i=cr+1;i<=n;i++)if(a[i]>a[cr]&&a[i]>x)break;
    for(int j=i+1;j<=n;j++)if(a[j]<a[cr])return 0;//导致在j之前cr不能弹出 
    return 1;
}
void dfs(int cr,int nw)
{
    if(cr>n)return;
    int x=a[cr],tnw=nw;bool cflag=0;
    int tsk[2][N];memcpy(tsk,stack,sizeof stack);
    if(stack[0][top[0]]>x&&stack[1][top[1]]<x)
    {
        rd(x,0);
        if(ps[nw])cflag=1,cd(nw);
//        printf("cr=%d 0\n",cr);
        dfs(cr+1,nw);
        if(flag){
            if(cflag){
                tot-=nw-tnw;top[0]+=nw-tnw;nw=tnw;
                memcpy(stack,tsk,sizeof tsk);cflag=0;
            }
            clr(x,0);
//            printf("ret cr=%d flag=1\n",cr);
        }
    }
    else if(stack[1][top[1]]>x&&stack[0][top[0]]<x)
    {
        rd(x,1);
        if(ps[nw])cflag=1,cd(nw);
//        printf("cr=%d 1\n",cr);
        dfs(cr+1,nw);
        if(flag){
            if(cflag){
                tot-=nw-tnw;top[1]+=nw-tnw;nw=tnw;
                memcpy(stack,tsk,sizeof tsk);cflag=0;
            }
            clr(x,1);
//            printf("ret cr=%d flag=1\n",cr);
        }
    }
    else if(stack[1][top[1]]<x&&stack[0][top[0]]<x)
    {
//        printf("cr=%d flag=1!\n",cr);
        flag=1;return;
    }
    else{
//        printf("stack0=%d stack1=%d(top0=%d top1=%d)\n",stack[0][top[0]],stack[1][top[1]],top[0],top[1]);
        if(check(cr,stack[1][top[1]]))
        {
            rd(x,0);
            if(ps[nw])cflag=1,cd(nw);
//            printf("cr=%d try0\n",cr);
            dfs(cr+1,nw);
            if(flag){
                if(cflag){
                    tot-=nw-tnw;top[0]+=nw-tnw;nw=tnw;
                    memcpy(stack,tsk,sizeof tsk);cflag=0;
                }
                clr(x,0);
            }
        }
        if(flag&&check(cr,stack[0][top[0]]))
        {
            flag=0;rd(x,1);
            if(ps[nw])cflag=1,cd(nw);
            dfs(cr+1,nw);
            if(flag){
                if(cflag){
                    tot-=nw-tnw;top[1]+=nw-tnw;nw=tnw;
                    memcpy(stack,tsk,sizeof tsk);cflag=0;
                }
                clr(x,1);
//                printf("cr=%d failtry1\n",cr);
            }
        }
        if(flag||(!check(cr,stack[0][top[0]])&&!check(cr,stack[1][top[1]])))flag=1;
    }
}
int main()
{
    scanf("%d",&n);int x;
    stack[0][0]=stack[1][0]=n+1;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    dfs(1,1);
    if(flag)printf("0");
    else for(int i=1;i<=tot;i++)printf("%c ",ch[i]);
    return 0;
}
View Code

然后发现只有30分。而且T1WA6。

在写搜索之前看了看TJ,体现在最后那个check里。就是如果a[1],a[2],a[3]满足a[2]>a[1],a[3]<a[1]的话,在a[3]来之前a[1]不能出栈,就被堵在了a[2]后面,从而GG。

但是这竟然就是唯一的GG条件!需要仔细思考。

这个性质和自己开始分析出来的浅显性质的不同之处:

1.自己的那个只有在过程中才能判断,因为一开始有升序,也可能前面小的先出栈,后面大的再进来。

  而正解的想法是把这个不确定因素也分析掉,得出如果在这个大的后面又有一个比最前面的更小的,就真的不行了。

    这样就能不在过程中判断而可以预先判断了。

2.自己对“两个栈”认识不足。明明可以把它看成一个主栈和一个辅助栈,用上一条判断出不能往主栈放就放在辅助栈里,可自己……

  没错,正解这样想的话就可以只关注元素能不能放在同一个栈中。然后想到只有两个栈,于是二分图染色就有了。

因为判掉了无解,所以最后的模拟可以开放又随便。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005;
int n,a[N],head[N],xnt,col[N],ps[N],now,f[N];
bool flag;
struct Edge{
    int next,to;
    Edge(int n=0,int t=0):next(n),to(t) {}
}edge[N*N];
void add(int x,int y)
{
    edge[++xnt]=Edge(head[x],y);head[x]=xnt;
    edge[++xnt]=Edge(head[y],x);head[y]=xnt;
}
void init()
{
    f[n]=n+1;
    for(int i=n-1;i;i--)
    {
        f[i]=min(f[i+1],a[i+1]);
        for(int j=i+1;j<=n;j++)
            if(a[j]>a[i]&&f[j]<a[i])add(i,j);
    }
}
void dfs(int cr)
{
    for(int i=head[cr],v;i;i=edge[i].next)
    {
        if(col[v=edge[i].to]==col[cr])
            {flag=1;return;}
        else if(!col[v])col[v]=(col[cr]^1),dfs(v);
        if(flag)return;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    init();
    for(int i=1;i<=n;i++) if(!col[i])
    {
        col[i]=2;dfs(i);if(flag)break;
    }
    if(flag){
        printf("0");return 0;
    }
    now=1;
    for(int i=1;i<=n;i++)
    {
        ps[a[i]]=col[i];
        if(col[i]==2)printf("a ");
        else printf("c ");
        while(ps[now]){
            if(ps[now++]==2)printf("b ");
            else printf("d ");
        }
    }
    return 0;
}
View Code

从代码的col和ps可以看出二分图染色和最后的模拟有一定程度的重复。其实可以大胆地直接模拟就行了。

(此方法源自洛谷用户cmd2001。在洛谷题解的第2页可以看到。)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005;
int n,r[N],ct,now,a[N],ta,b[N],tb,tot;
char ch[N<<1];
bool flag;
bool check(int cr)
{
    int i;
    for(i=cr+1;i<=n;i++)if(r[i]>r[cr]&&r[i]>b[tb])break;
    for(int j=i+1;j<=n;j++)if(r[j]<r[cr])return 0;
    return 1;
}
int main()
{
    scanf("%d",&n);int lm=(n<<1);now=1;ct=1;
    for(int i=1;i<=n;i++)scanf("%d",&r[i]);
    a[0]=b[0]=n+1;
    for(int i=1;i<=lm;i++)
    {
        if(a[ta]==now)
        {
            ch[++tot]='b';ta--;now++;
            continue;
        }
        if(b[tb]==now)
        {
            ch[++tot]='d';tb--;now++;
            continue;
        }
        if(ct<=n&&r[ct]<a[ta]&&check(ct))
        {
            a[++ta]=r[ct++];ch[++tot]='a';
            continue;
        }
        if(ct<=n&&r[ct]<b[tb])
        {
            b[++tb]=r[ct++];ch[++tot]='c';
            continue;
        }
        flag=1;break;
    }
    if(flag)printf("0");
    else for(int i=1;i<=lm;i++)printf("%c ",ch[i]);
    return 0;
}
View Code

这道题教会我们要好好分析性质。别只分析出一个浅显的了!

posted on 2018-06-22 15:46  Narh  阅读(176)  评论(0编辑  收藏  举报

导航