洛谷1155 雙棧排序(二分圖染色)
阿新 • • 發佈:2018-12-14
(因為洛谷的格式問題所以不貼題目描述了)
【題目分析】
首先我們發現,對於所有輸出的順序,我們可以視作一個佇列,所以b、d操作就可視作將stack1、stack2的棧頂元素彈入que的隊尾,如果入隊順序可以為1-n,那麼就可行,否則不行。
然後考慮順序,這裡結合二分圖(非匹配)的思想,將1~n視作n個點,現在就要對這n個點進行黑白染色(分別代表兩個集合),現在需要思考的就是如何將所有點分在兩個集合中。
首先判斷是否有解,考慮對於任意兩個數a[i]和a[j]來說,它們不能壓入同一個棧中的充要條件是什麼(注意沒有必要使它們同時存在於同一個棧中,只是壓入了同一個棧)。實際上,這個條件p是:存在一個k,使得i<j<k且a[k]<a[i]<a[j]。
有了這個條件我們就可以進行染色了,如果存在一個情況是i<j<k且a[k]<a[i]<a[j],那麼i和j肯定沒法在同一個棧中,所以我們將i和j連邊(注意i到j要連一條,同樣j到i也要連一條)
考慮了有解之後就是尋找字典序最小的解,因為壓入stack1的操作為a,所以我們希望編號小的節點優先壓入stack1中,然後我們發現對於這樣一個圖,不同強連通分量之間的顏色是互不影響的,所以我們找到每一個強連通分量中編號最小的點將其壓入stack1,染色為1,然後dfs一遍即可。
最後一點小優化:我們肯定不可能去列舉所有的i<j<k且a[k]<a[i]<a[j],因為這樣複雜度會上升到O(n^3),所以我們用一個數組b記錄預處理出的所有a[i]到a[n]的最小值,然後掃一遍看看是否滿足b[i]<a[i]且a[i]<a[j]。
【程式碼~】
#include<bits/stdc++.h> using namespace std; const int MAXN=1e3+10; const int MAXM=2e6+10; int n,m,cnt; int head[MAXN],col[MAXN]; int nxt[MAXM],to[MAXM]; int a[MAXN],b[MAXN],c[MAXN]; int stk1[MAXN],stk2[MAXN],top1,top2; int minn[MAXN]; void add(int x,int y) { cnt++; nxt[cnt]=head[x]; head[x]=cnt; to[cnt]=y; } void dfs(int u) { for(int i=head[u];i!=-1;i=nxt[i]) { int v=to[i]; if(!c[v]) { c[v]=3-c[u]; dfs(v); } if(c[u]==c[v]) { cout<<0; exit(0); } } } int main() { memset(head,-1,sizeof(head)); scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); minn[n]=a[n]; for(int i=n-1;i>=1;--i) minn[i]=min(minn[i+1],a[i]); for(int i=1;i<n-1;++i) for(int j=i+1;j<n;++j) if(a[i]<a[j]&&minn[j+1]<min(a[i],a[j])) add(i,j),add(j,i); for(int i=1;i<=n;++i) if(!c[i]) { c[i]=1; dfs(i); } int now=1; for(int i=1;i<=n;++i) { if(c[i]==1) { stk1[++top1]=a[i]; cout<<"a "; } else { stk2[++top2]=a[i]; cout<<"c "; } while(stk1[top1]==now||stk2[top2]==now) { if(stk1[top1]==now) { now++; top1--; cout<<"b "; } if(stk2[top2]==now) { now++; top2--; cout<<"d "; } } } return 0; }