【問題描述】
用兩個棧使乙個1...n的排列變得有序。
一共有四個操作:
a.stack1.push() 讀入乙個放入棧一
b.stack1.pop() 彈出棧一放入輸出序列
c.stack2.push() 讀入乙個放入棧二
d.stack2.pop() 彈出棧二放入輸出序列
給你乙個初始的排列,
求乙個字典序最小的操作序列使得變得有序,若沒有滿足條件的操作序列,輸出'0'。
sample.in sample.out
4 1 3 2 4 a b a a b b a b
4 2 3 4 1 0
3 2 3 1 a c a b b d
【分析】
嗯嗯嗯...這題好有意思啊...然後開始埋頭苦幹,發現好神奇啊!
1.棧原來真的可以排序誒!
2.雙棧原來也可以排序誒!
3.原來第二個樣例真的不能排序誒!
....於是沉默了....於是開始糾結....什麼樣的資料不能用雙棧排序呢?
........
思考了很久很久.....似乎想不出來什麼東西....
不過我在構造中發現....每當乙個棧要掛的時候,往往可以禍水東引,將即將堵住的元素放到另乙個棧中去,每次兩個棧都堵住的時候...恩恩,雙棧排序就掛了!
於是開始想另乙個問題:什麼樣的資料不能用單棧排序....
隨便造了乙個:2 3 1 恩,就掛了。
因為2應該在3前出去,但是因為1在後面,所以2出不去,就被3堵住了....
所以只要有 i
【題外】【嚴謹證明】【copy from codevs】【不想看的記住這個結論就好了】
考慮對於任意兩個數a[i]和a[j]來說,它們不能壓入同乙個棧中的充要條件是什麼(注意沒有必要使它們同時存在於同乙個棧中,只是壓入了同乙個棧).
實際上,這個條件p是:存在乙個k,使得i
首先證明充分性,即如果滿足條件p,那麼這兩個數一定不能壓入同乙個棧.這個結論很顯然,使用反證法可證.
假設這兩個數壓入了同乙個棧,那麼在壓入a[k]的時候棧內情況如下:
…a[i]…a[j]…所以如果遇到這樣的 i,j,它們一定不能丟到乙個棧裡面去,於是給它們並查集連個虛點?或者連條什麼邊[二分圖染色]。因為a[k]比a[i]和a[j]都小,所以很顯然,當a[k]沒有被彈出的時候,另外兩個數也都不能被彈出(否則輸出序列中的數字順序就不是1,2,3,…,n了).
而之後,無論其它的數字在什麼時候被彈出,a[j]總是會在a[i]之前彈出.而a[j]>a[i],這顯然是不正確的.
接下來證明必要性.也就是:如果兩個數不可以壓入同乙個棧,那麼它們一定滿足條件p.
這裡我們來證明它的逆否命題,也就是"如果不滿足條件p,那麼這兩個數一定可以壓入同乙個棧."
不滿足條件p有兩種情況: 1. 對於任意ia[i]; 2. 對於任意ia[j].
第一種情況下,很顯然,在a[k]被壓入棧的時候,a[i]已經被彈出棧.那麼,a[k]不會對a[j]產生任何影響(這裡可能有點亂,因為看起來,當a[j]
唔...下面就好辦啦....[我打的二分圖染色,就講這個哈]
二分圖染色 [只有兩種顏色1,0] 就是在每乙個連通塊上,對乙個點染上黑色[belong[x]=1],然後對所有連的邊染上和它不同的顏色[belong[v]=1^belong[x]],當然還得給每個元素下乙個是否已經找到顏色的標記sure,以免多次染色和漏掉矛盾。[矛盾的情況很好辦咯...就是發現兩個已經確定的,而且練了邊的點染成了同乙個顏色!....然後就槍斃了...]
因為要求操作序列的字典序最小,所以我們希望前面的元素能盡可能放進棧一中,假設棧一中的都是顏色為黑的,那麼只需要按照順序對每乙個不曾染過色的點染成黑色,然後拓展它所在的連通塊即可。
最後,我們都知道了所有點在哪個棧中....就可以開始開心的模擬了!
記錄乙個cot表示現在希望彈出的元素是幾,發現可以彈出cot的時候一直彈彈彈[ 彈走魚尾紋 ] 就可以了,具體還不懂就看**咯.....[有貼心小注釋]
1 #include2 #include3view code4using
namespace
std;
56 inline int
in()
1213
const
int maxn=1010;14
15struct nodenode[maxn*maxn];
1819
#define now node[point].data
20#define then node[point].next
2122
int n,cnt,t1,t2,cot=1;23
inta[maxn],head[maxn],belong[maxn];
24int
f[maxn],s1[maxn],s2[maxn];
25bool
sure[maxn],over;
2627 inline int min(int a,int
b)30
31 inline void add(int u,int v)
3536
void dfs(int
x)42}43
else
//如果相鄰的點未曾染色
44 belong[now]=1-belong[x],dfs(now); //
染色,並擴充套件所在連通分塊 45}
4647 inline void pop()54}
5556
intmain()73}
7475
for(int i=1;i<=n;i++)
82return0;
83 }
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隊首元素都...