洛谷P1155 [NOIP2008 提高组] 双栈排序
题意
有两个栈,四个操作
a.把数据压入栈1
b.弹出1中栈顶数据
c.把数据压入2
d.弹出2中栈顶数据
要通过一系列操作,将读入的n个1~n的不重复的数据升序排列,如果无法排列则输出0,否则输出字典序最小的操作顺序。
思路
本题分为两个步骤:1.判断能否成功排序;2.输出最小字典序的操作。
假定只有一个栈,那么什么时候会出现无法排序的情况呢?
手推几组数据,便可以发现,当i<j<k时,a[k]<a[i]<a[j]便会出现矛盾,此时i和j必定要有一个去第二个栈中
有两个栈,两个数据不能在同一个栈中,我们会想到什么?
二分图嘛
所以我们在i,j上连一条边,我们先预处理出所有的连边:
minn[n+1]=n+1; for(int i=n;i>=1;i--) minn[i]=min(minn[i+1],a[i]); for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) if(minn[j+1]<a[i] && a[i]<a[j]){ add(i,j); add(j,i); }
其中,minn数组表示在该点后所有数据中的最小值,也就是上面的k
然后就通过染色的方法,判断该数据能不能排序,如果染色过程中产生了冲突,也就是说一个数据既要在栈1又要在栈2中,那么肯定无法排序,输出0即可
之后就是进行操作了,因为我们要求字典序最小,所以肯定尽量入栈1,出的时候也尽量先出栈1
而如果要求能升序排列,那么每一个栈中的数据必须是降序排列的,而如果突然比前面大了,我们就要把前面比它小的都先输出来
输出的时候我们记录现在要输出的数,如果在栈1顶就操作b,否则必然在栈2顶,就操作d
输出完后把该点入到对应的栈。
最后把所有的数据按照顺序输出即可~
void add(int u,int v){ tail++; nxt[tail]=head[u]; head[u]=tail; to[tail]=v; } void dfs(int u,int cl){ if(!flag) return; if(vis[u]){ if(color[u]!=cl) flag=0; return; } vis[u]=1; color[u]=cl; for(int i=head[u];i;i=nxt[i]){ int v=to[i]; dfs(v,1-cl); } } int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; minn[n+1]=n+1; for(int i=n;i>=1;i--) minn[i]=min(minn[i+1],a[i]); for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) if(minn[j+1]<a[i] && a[i]<a[j]){ add(i,j); add(j,i); } for(int i=1;i<=n;i++){ if(!vis[i]){ flag=1; dfs(i,0); } if(!flag) break; } if(!flag){ printf("0\n"); return 0; } else{ int now=1; set1[0]=set2[0]=n+1; for(int i=1;i<=n;i++) if(!color[i]){ while(a[i]>set1[top1] && now<=n) if(set1[top1]==now){ printf("b "); now++; top1--; } else{ printf("d "); now++; top2--; } printf("a "); set1[++top1]=a[i]; } else{ while(set1[top1]==now && now<=n){ printf("b "); now++; top1--; } while((a[i]>set2[top2] || set1[top1]==now) && now<=n) if(set1[top1]==now){ printf("b "); now++; top1--; } else{ printf("d "); now++; top2--; } printf("c "); set2[++top2]=a[i]; } while(now<=n) if(set1[top1]==now){ printf("b "); now++; top1--; } else{ printf("d "); now++; top2--; } }

浙公网安备 33010602011771号