noip2008 雙棧排序

2022-05-21 12:57:09 字數 2881 閱讀 2443

\(tom\)最近在研究乙個有趣的排序問題。如圖所示,通過\(2\)個棧\(s_1\)和\(s_2\),\(tom\)希望借助以下\(4\)種操作實現將輸入序列公升序排序。

操作\(a\)

如果輸入序列不為空,將第乙個元素壓入棧\(s_1\)

操作\(b\)

如果棧\(s_1\)不為空,將\(s_1\)棧頂元素彈出至輸出序列

操作\(c\)

如果輸入序列不為空,將第乙個元素壓入棧\(s_2\)

操作\(d\)

如果棧\(s_2\)不為空,將\(s_2\)棧頂元素彈出至輸出序列

如果乙個$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$

輸入的第一行是乙個整數n。

第二行有$n$個用空格隔開的正整數,構成乙個$1$到$n$的排列。

輸出共一行,如果輸入的排列不是「可雙棧排序排列」,輸出數字$0$;否則輸出字典序最小的操作序列,每兩個操作之間用空格隔開,行尾沒有空格。

當然,這樣的操作序列有可能有幾個,對於上例$(1,3,2,4)$,$a,c,c,b,a,d,d,b$是另外乙個可行的操作序列。$tom$希望知道其中字典序最小的操作序列是什麼。

把以前不會寫的$noip$題目補一下~其實現在我也還是不會。

感覺這道題思路非常神……首先,貪心的選顯然是錯誤的,因為不能保證可以構造出一組可行解。然後……這道題到底該怎麼做啊啊啊!

於是到最後我還是去看題解了……

這道題首先要證明乙個結論:對於任意兩個數$a_i$和$a_j$來說,它們不能壓入同乙個棧(不僅僅指同時在棧中,而是壓入了同乙個棧)中的充要條件是:存在乙個$k$,使得$i首先證明充分性。由於$a_k然後證明必要性。這個東西比較難證,於是我們可以轉而證明它的逆否命題:若對於任意的$i,那麼他們可以壓入乙個棧。

對於不滿足$a_ka_i$,二是$a_i>a_j$。

對於第一種情況,由於$a_k>a_i$,所以在$a_k$入棧之前$a_i$已經可以彈出,於是可以壓入同乙個棧。

對於第二種情況,顯然不論$a_k$大小如何,都可以先把$a_i$和$a_j$壓入同乙個棧,$a_j$會先於$a_i$彈出。

於是該命題的逆否命題得證,所以原命題得證。

於是,這個問題就變成了二分圖的問題了。把不可以壓入同乙個棧的點連邊,然後判斷乙個圖不是二分圖就無解,否則有解。

然後就是字典序最小的問題了。由於字典序最小需要靠前的數盡量往第乙個棧裡丟,所以可以從前到後判斷一下,在二分圖中$dfs$一遍。

於是這道題就這麼愉快的解決辣!話說考場上碰到這種題我真的寫得出來嗎?

小結一下:1.碰到一類毫無思路的問題時,要推一推題目中有沒有什麼奇妙的性質;

2.發現自己的複雜度遠過低時,一定要多檢查幾遍;

3.一定要先寫暴力,可以用來驗證自己猜的結論。

下面貼**:

#include#include#include#include#include#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)

#define n 1010

using namespace std;

typedef long long llg;

int n,a[n<<1],wa[n],co[n],b[n],lb;

int head[n],next[n*n],to[n*n],tt;

int s1[n],t1,s2[n],t2,now=1;

bool vis[n];

int getint()

void link(int x,int y)

bool ran(int u)

void dfs(int u,bool w)

int main()

}for(int i=1;i<=n;i++)

if(!vis[i]) dfs(i,co[i]!=1);

for(int i=1;i<=n;i++)

}for(int i=1;i<=lb;i++) printf("%c ",'a'+b[i]-1);

}

upd:最近發現被hack了……於是就重寫了乙份,**如下:

#include#include#include#include#include#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)

#define n 1010

using namespace std;

typedef long long llg;

int n,a[n<<1],wa[n],co[n],b[n],lb;

int hd[n],nt[n*n],to[n*n],tt;

int s1[n],t1,s2[n],t2,now=1;

bool vis[n];

int getint()

void link(int x,int y)

void ran(int u)

else if(!co[v]) co[v]=3-co[u],ran(v);

}int main()

s1[++t1]=a[i],b[++lb]=1;

} if(co[i]==2)

s2[++t2]=a[i]; b[++lb]=3;

} while(s1[t1]==now) t1--,b[++lb]=2,now++;

} while(s1[t1]==now || s2[t2]==now)

for(int i=1;i<=lb;i++) printf("%c ",'a'+b[i]-1);

}

NOIP2008 雙棧排序

link 考慮什麼時候 i,j i,j i,j 可以被放到同乙個棧裡面 我們只需要考慮如果我們把 i,j i,j i,j 放一起後面會不會出現矛盾 假設有 i j i j k,觀察每一種排列,只有當 a k a kak 的時候,是會出現問題的,我們不能構造一種方案滿足這個條件 所以我們通過計算字尾最...

noip 2008 雙棧排序

題目大意 給定n和一串數字,這串數字是乙個1 n的排列。現在要用兩個棧給這些數字排序。首先先判斷是否有解,有解的話再輸出字典序最小的方案 入棧1,輸出a,出棧1,輸出b 入棧2,輸出c,出棧2,輸出d 分析 首先必然要先考慮是否有解。對於沒有解的情況,必然是當到了某乙個數x0時,棧1,棧2隊首元素都...

noip 2008 雙棧排序

題目大意 給定n和一串數字,這串數字是乙個1 n的排列。現在要用兩個棧給這些數字排序。首先先判斷是否有解,有解的話再輸出字典序最小的方案 入棧1,輸出a,出棧1,輸出b 入棧2,輸出c,出棧2,輸出d 分析 首先必然要先考慮是否有解。對於沒有解的情況,必然是當到了某乙個數x0時,棧1,棧2隊首元素都...