Description
Tom最近在研究一個有趣的排序問題。如圖所示,通過2個棧S1和S2,Tom希望藉助以下4種操作實現將輸入序列升序排序。
操作a
如果輸入序列不為空白,將第一個元素壓入棧S1
操作b
如果棧S1不為空白,將S1棧頂元素彈出至輸出序列
操作c
如果輸入序列不為空白,將第一個元素壓入棧S2
操作d
如果棧S2不為空白,將S2棧頂元素彈出至輸出序列
如果一個1~n的排列P可以通過一系列操作使得輸出序列為1,2,…,(n-1),n,Tom就稱P是一個“可雙棧排序排列”。例如(1,3,2,4)就是一個“可雙棧排序序列”,而(2,3,4,1)不是。下圖描述了一個將(1,3,2,4)排序的操作序列:< a,c,c,b,a,d,d,b>
當然,這樣的操作序列有可能有幾個,對於上例(1,3,2,4),< a,c,c,b,a,d,d,b>是另外一個可行的操作序列。Tom希望知道其中字典序最小的操作序列是什麼 Input Format
輸入檔案twostack.in的第一行是一個整數n。 第二行有n個用空格隔開的正整數,構成一個1~n的排列 Output Format
輸出檔案twostack.out共一行,如果輸入的排列不是“可雙棧排序排列”,輸出數字0;否則輸出字典序最小的操作序列,每兩個操作之間用空格隔開,行尾沒有空格 Sample Input
【輸入範例1】41 3 2 4【輸入範例2】42 3 4 1【輸入範例3】32 3 1
Sample Output
輸出範例1a b a a b b a b輸出範例20輸出範例3a c a b b d
Hint
【限制】30%的資料滿足: n<=10
50%的資料滿足: n<=50
100%的資料滿足: n<=1000
【題解】
個人覺得這題想法有點難(反正我是想不到)
二分圖染色+類比
先對每一對永遠都不可能進同一個棧的兩個數建邊( 若存在k,滿足i<j<k,使得S[k]<S[i]<S[j]則i,j永遠不能再同一個棧)
對這張圖進行二分圖染色(這樣就可以把矛盾的兩個數分在兩個棧裡面了)如果是二分圖則可以雙棧排序(接下來輸出就是類比了)
染色的時候 按編號遞增尋找未染色的點初始顏色為0,這樣可以保證字典序
詳見代碼
#include <algorithm>#include <iostream>#include <cstdlib>#include <cstring>#include <string>#include <cstdio>#include <queue>#include <ctime>#include <cmath>#include <vector>using namespace std;int i,j,k,l,m,n,pd,b;int a[1005],col[1005],s[3][1005],num[3];vector <int> road[1005];void work(int u) { if (pd==1) return; for (int i=0;i<road[u].size();i++) { int v=road[u][i]; if (!col[v]) col[v]=3-col[u],work(v); if (col[v]!=3-col[u]) {b=0;pd=1;return;} } }int main() { scanf("%d",&n); for (i=1;i<=n;i++) scanf("%d",&a[i]); k=1e9; for (j=n;j;j--) { k=min(k,a[j]); for (i=1;i<=j-1;i++) if (a[i]<a[j]&&a[i]>k) road[i].push_back(j),road[j].push_back(i); }b=1;for (i=1;i<=n;i++) { if (!col[i]) col[i]=1,pd=0,work(i); if (b==0) {printf("0");return 0;} } for (j=i=1;i<=n;i++) { if (col[i]==1) printf("a ");else printf("c "); s[col[i]][++num[col[i]]]=i; for (;a[s[1][num[1]]]==j||a[s[2][num[2]]]==j;j++) { if (a[s[1][num[1]]]==j) printf("b "),--num[1]; else printf("d "),--num[2]; } } }