题目: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; }
然后发现只有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; }
然后发现只有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; }
然后发现只有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; }
从代码的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; }
这道题教会我们要好好分析性质。别只分析出一个浅显的了!