题目描述
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希望知道其中字典序最小的操作序列是什么。
输入输出格式
输入格式:
输入文件twostack.in的第一行是一个整数n。
第二行有n个用空格隔开的正整数,构成一个1~n的排列。
输出格式:
输出文件twostack.out共一行,如果输入的排列不是“可双栈排序排列”,输出数字0;否则输出字典序最小的操作序列,每两个操作之间用空格隔开,行尾没有空格。
输入输出样例
输入样例#1:
【输入样例1】
4
1 3 2 4
【输入样例2】
4
2 3 4 1
【输入样例3】
3
2 3 1
输出样例#1:
【输出样例1】
a b a a b b a b
【输出样例2】
0
【输出样例3】
a c a b b d
说明
30%的数据满足: n<=10
50%的数据满足: n<=50
100%的数据满足: n<=1000
【分析】
思路题…反正我想不出来
[参考sicily]
1.先考虑单栈情况:
定理:考虑对于任意两个数q[i]和q[j],它们不能压入同一个栈中的充要条件: 存在一个k,使得i < j < k且q[k] < q[i] < q[j]。
证明:
充分性:即如果满足上述条件,那么q[i]和q[j]一定不能压入同一个栈。
- 双栈情况:
2.1 判断是否有解和任意两个数能否压入同一个栈
(1) 对任意两个数q[i]和q[j],若存在一个k,使得i < j < k且q[k ] < q[i] < q[j],则这两个数分别入s1栈和s2栈
(2) 将s1栈和s2栈中的数字分成两个顶点子集,同一顶点子集内不会出现任何连边,即不能压入同一个栈的所有数字被分到了两个栈中。任意两个不在同一栈的数字间连边。此时我们只考虑检查是否有解,所以只要花O(n)时间检查这个图是不是二分图,就可以得知是否有解了。
*(二分图是一种特殊类型的图:图中的顶点集被划分成X与Y两个子集,图中每条边的两个端点一定是一个属于X而另一个属于Y。二分图的匹配是求边的一个子集,该子集中的任意两条边都没有公共的端点。)
2.2 解题步骤:
(1) 检查数列中的数字是否满足进入同一个栈的条件,如果不满足则将边连上,构造二分图。
(2) 用dfs染色,把二分图染成1和2两种颜色,使得染色为1的结点被压入s1栈,染色为2结点被压入s2栈。具体:每次选取一个未染色的编号最小的结点,将它染色为1,并从它开始dfs染色,直到所有结点都被染色为止。这样,我们就得到了每个结点应该压入哪个栈中。注意dfs结束之后未被染色的数字是可以被放入同一个栈的,所以优先染色为1。如果发现某一个数字两次要染不同的颜色,则为不能输出。
(3) 模拟出栈操作。
3.特殊情况与注意点:
3.1 为了判断是否满足同一个栈的条件,需要对数组进行预先处理,得到一个记录每一位数字之后最小数字的数列。
3.2 模拟输出的时候,考虑这样一种情况:此时将栈1排空了,可以压入数字,也可以继续输出栈2的数字。如果模拟的时候直接顺序排列a, b, c, d四种操作,则b结束后,因为预先染色不能压入c,则进行d操作了。这是不对的,因为需要先压入栈1,再排出栈2,顺序应该是a d而不是d a。从这个角度来说,找到的很多代码其实是错误的。需要增加一个循环元素,如果排空栈1之后可以压入则回到循环顶部先压入s1在去输出栈2的数字。
【代码】
(重构版本)