這題可是真神。
讓字典序最小,當然盡量進S1
那什麼時候必須進S2呢。
a[i]和a[j] 不能壓入同一個棧⇔存在一個k,使得i< j< k且a[k]< a[i]< a[j]
因為一個數只能進出一次,k要排在前面所以彈出k時i和j都在棧裡,如果兩者在同一個棧彈出後順序就錯誤了。
因此我們把這種一定不同棧的i,j連一條邊,就得到了一個圖,然後我們去判斷這張圖是不是二分圖(黑白點染色),顯然不是二分圖則一定不能滿足雙棧排序。然後我們去類比彈棧就好啦
tips:判斷i,j是否不同棧時,如果迴圈k是O(n^3),顯然會T,因此我們預先處理出一個mn數組,表示尾碼最小值。
最後類比輸出答案為什麼一定是字典序最小的呢。因為你在染色時,讓標號小的盡量進入了第一個棧,因此你只需要類比何時彈出即可。
#include <bits/stdc++.h>using namespace std;#define N 1010#define ll long long#define inf 0x3f3f3f3finline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*f;}int n,a[N],mn[N],h[N],num=0,col[N],s1[N],s2[N],top1=0,top2=0;struct edge{ int to,next;}data[N*N<<1];inline void add(int x,int y){ data[++num].to=y;data[num].next=h[x];h[x]=num; data[++num].to=x;data[num].next=h[y];h[y]=num;}bool dfs(int x){//二分圖染色 for(int i=h[x];i;i=data[i].next){ int y=data[i].to;if(col[y]==col[x]) return 0; if(col[y]!=-1) continue; col[y]=col[x]^1;if(!dfs(y)) return 0; }return 1;}int main(){// freopen("a.in","r",stdin); n=read();for(int i=1;i<=n;++i) a[i]=read();mn[n+1]=inf; for(int i=n;i>=1;--i) mn[i]=min(a[i],mn[i+1]); for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j)//i,j一定不同棧 if(a[i]<a[j]&&mn[j+1]<a[i]) add(i,j); memset(col,-1,sizeof(col)); for(int i=1;i<=n;++i) if(col[i]==-1){//沒染過色,優先進第一個棧 col[i]=0;if(!dfs(i)){puts("0");return 0;}; }int now=1; for(int i=1;i<=n;++i){//類比輸出 if(col[i]){printf("c ");s2[++top2]=a[i];} else{printf("a ");s1[++top1]=a[i];} while(s1[top1]==now||s2[top2]==now){ if(s1[top1]==now){putchar('b');--top1;} else {putchar('d');--top2;} if(now!=n) putchar(' ');now++; } }return 0;}