双栈排序(codevs 1170)题解

【问题描述】

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.

 

转载于:https://www.cnblogs.com/PengBoLiuXu/p/4522400.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值