双栈排序

首先有一个比较错误的贪心思路,就是假设我们前面的操作是最优方案,对于当前的元素,能放第一个栈就放第一个栈;如果不行那么第一个栈的栈顶元素小于当前元素,此时如果能弹出这个栈顶元素就弹出这个栈顶元素,然后重新从最开始的位置开始考虑;否则尝试将当前元素放入第二个栈;如果不行则第二个栈的栈顶元素小于当前元素,直接将第二个栈的栈顶元素弹出,然后重新从最开始的位置开始考虑

这个贪心的方法就有可能导致一个本来可以排序的序列,不能通过这个方法成功排序

那么我们怎么想呢?

假设我们已经对每一个数分到的栈的编号确定了,假设我们前面的操作方案已经是最优的了,此时两个栈的元素一定都是单调递减的,对于当前元素,如果他能够放入其想放入的栈,那么就直接放入;否则其想放入的栈的栈顶元素一定小于这个元素,所以我们要将这个元素弹出,设栈顶元素为\(x\),当前考虑的元素为\(y\),则\(y>x\),如果\(z<x\)\(z\)还没有入栈(就是在\(y\)的后面被考虑),那么肯定就无解了,否则的话\(z\)要么已经被弹出,要么就在两个栈中某个栈的栈顶,我们直接弹出就好了(注意两个栈的元素单调递减)

从上面这个伪过程可以看出,对于两个下标\(i<j\),如果\(a_i<a_j\)且存在\(k>j\)\(a_k<a_i\),那么\(i,j\)一定不会被分到同一组

实际上这个结论是充要的。必要性很容易证明,下面证明充分性,也就是证明如果\(i,j\)一定不会被分到同一组,那么如果\(a_i<a_j\)且存在\(k>j\)\(a_k<a_i\)

我们证明其逆否命题:如果\(a_i>a_j\)或者对任意\(k>j\)\(a_k>a_i\)\(i,j\)可能被分到同一组(即一定能找到一个解)

我们考虑已经分好了的两个组,其中每个组中的任意二元组都不满足条件(注意\(k\)不一定要局限于分组之后的同一组,而是对于原序列来说,就是\(k\)也可以是另一组的数字),即若\(i<j\)\(a_i>a_j\)或者对任意\(k>j\)\(a_k>a_i\)

此时每个元素进入栈的编号都已经确定,我们依次模拟。假设前面的模拟都合法(也就是说此时两个栈的元素都单调递减),考虑当前元素,不妨设其要进入第一个栈,如果栈顶元素大于当前元素,那么直接放入,满足栈的元素单调递减而且此次操作合法;否则的话,考虑弹出第一个栈的栈顶元素,设栈顶元素为\(x\),由假设,对任意\(z<x\),其要么已经被弹出过了,要么在第二个栈中,也就是说如果\(x\)不是当前能够被弹出的元素的话,那么第二个栈的栈顶元素一定是能够被弹出的元素,所以此时总能够弹出元素,我们一直弹出直到当前元素能够放入第一个栈为止,最后仍然满足所有操作合法且栈的元素单调递减

综上,我们对任意的不满足条件的两个组,我们一定能够找到一种合法的方案

所以这个时候,我们对不可能在一个组的\(i,j\)连边,然后就是一个二分图染色的问题了

现在我们要求字典序最小,假设我们前面已经是最优的操作方案了,那么对于当前元素,

1.如果这个元素已经被确定放在哪个栈中了

1).如果是第一个栈,那么我们能放入则放入(根据我们上面的证明,此时放入一定可以是解,而且一定是字典序最小的解);否则的话我们考虑弹出,此时的操作顺序是固定的,最后将这个元素放入第一个栈就好了;

2).如果是第二个栈

①.如果当前元素小于栈顶元素,我们考虑先将第一个栈能弹出的元素弹出,然后再放入当前元素,此时一定可以找到一个解(因为此时还是满足两个栈的元素单调递减),而且字典序最小

②.如果当前元素大于栈顶元素,我们考虑将栈顶元素弹出,此时的操作顺序在到达①之前都是固定的,之后再按①考虑就好了

2.如果这个元素没有被确定放入哪个栈中

1).如果这个元素比第一个栈的元素小,显然直接放入,字典序最小且一定有解

2).如果这个元素比第一个栈的栈顶元素大,那么也不会将这个元素放在第二个栈中,这是因为如果能放入第二个栈中,就说明第二个栈的栈顶元素大于当前元素,而当前元素大于第一个栈的栈顶元素,从而第一个栈的栈顶元素此时可以弹出,那么我们宁愿弹出(此时字典序最小而且仍然能找到解,因为两个栈的元素仍然单调递减),如果都不能放入第二个栈中,那么当前操作就是固定的(要么弹出第一个栈,要么弹出第二个栈),不用考虑了

posted @ 2023-12-09 11:11  最爱丁珰  阅读(4)  评论(0编辑  收藏  举报