【问题描述】
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希望知道其中字典序最小的操作序列是什么。
【样例输入1】
4
1 3 2 4
【样例输出1】
a b a a b b a b
【样例输入2】
4
2 3 4 1
【样例输出2】
0
【样例输入3】
3
2 3 1
【样例输出3】
a c a b b d
【解题思路】
本题为NOIP2008提高组第四题,初看觉得这应该是有史以来最水的提高组最后一题了,这不是模拟吗?只不过模拟的东西多了一点而已,然而,当我写着写着,就发现模拟的有点不对劲了……
好吧,这道题的正解应该是图的染色+模拟……
是的,没错,你没有看错,第四题居然要模拟!!!
染色其实是一个深搜的过程,染色是为了区分程序从头到尾始终不能放在一个栈中的数,然后为了保证是字典序最小,因此尽量往栈1中放,如果有始终无法放到同一个栈中的数被放到了同一个栈(这里的被放到同一个栈是指的染成同一种颜色),那么就无解,输出0。(染色的方法可以百度floodfill或者种子染色法)
那么问题来了……挖掘机哪家……不对……怎么区分始终不能放在一个栈中的数呢?
如果存在k使得i<j<k且ak<ai<aj则ai和aj不能进入同一个栈。
我们可以手推一下,如果k>j>i说明,j在i的后面进栈,k在j的后面进栈,而此时ai<aj,那么如果ai和aj进了同一个栈,那么就会是aj先出栈,就肯定不是从小到大排好序的了。
染完色后就好办了,直接模拟进栈出栈的过程……如果是最小值,就出,否则就进,然后因为已经染了色了,根据染的色判断它是进(出)栈1还是栈2,然后输出相应的字符即可。
现在还有一个最最担心的问题,怎么确定这个数是不是当前需要出栈的数(即最小值)?好吧……其实是我眼睛瞎了,是的,我为此纠结了半天,然后看题目……一个1~n的排列P……直接最小值赋值为1,然后出栈了就Inc……
【代码实现】
1 uses math; 2 var flag:array[1..1000,1..1000] of boolean; 3 q1,q2,color,a,f:array[1..1001] of longint; 4 n,i,j,sum,t1,t2:longint; 5 procedure dfs(x,c:longint); 6 var i:longint; 7 begin 8 color[x]:=c; 9 for i:=1 to n do 10 if flag[x,i] then 11 begin 12 if color[i]=c then 13 begin 14 writeln(0); 15 halt; 16 end; 17 if color[i]=0 then 18 dfs(i,3-c); 19 end; 20 end; 21 begin 22 readln(n); 23 for i:=1 to n do 24 read(a[i]); 25 f[n+1]:=maxlongint; 26 for i:=n downto 1 do 27 f[i]:=min(a[i],f[i+1]); 28 for i:=1 to n-1 do 29 for j:=i+1 to n do 30 if (a[i]<a[j])and(f[j+1]<a[i]) then 31 begin 32 flag[i,j]:=true; 33 flag[j,i]:=true; 34 end;//初始化,哪些是始终无法进同一个栈的 35 for i:=1 to n do 36 if color[i]=0 then 37 dfs(i,1);//给每一个数染色 38 sum:=1; 39 for i:=1 to n do 40 begin 41 if color[i]=1 then 42 begin 43 inc(t1); 44 q1[t1]:=a[i]; 45 write(‘a ‘); 46 end 47 else 48 begin 49 inc(t2); 50 q2[t2]:=a[i]; 51 write(‘c ‘); 52 end; 53 while ((t1<>0)and(q1[t1]=sum))or((t2<>0)and(q2[t2]=sum)) do 54 begin 55 if (0<>t1)and(q1[t1]=sum) then 56 begin 57 dec(t1); 58 write(‘b ‘); 59 end 60 else 61 begin 62 dec(t2); 63 write(‘d ‘); 64 end; 65 inc(sum);//最小值 66 end; 67 end;//最恶心的模拟…… 68 end.