题目(转自洛谷)
Tom最近在研究一个有趣的排序问题。如图所示,通过2个栈S1和S2 ,Tom希望借助以下4种操作实现将输入序列升序排序
操作a
如果输入序列不为空,将第一个元素压入栈S1
操作b
如果栈S1空,将S1栈顶元素弹出至输出序列
操作c
如果输入序列不为空,将第一个元素压入栈S 2
操作d
如果栈S2不为空,将S2栈顶元素弹出至输出序列
如果一个1−n的排列P可以通过一系列操作使得输出序列为1,2,…,(n-1),n1,2,…,(n−1),n,Tom就称PP是一个“可双栈排序排列”。例如(1,3,2,4)(1,3,2,4)就是一个“可双栈排序序列”,而(2,3,4,1)(2,3,4,1)不是。下图描述了一个将(1,3,2,4)(1,3,2,4)排序的操作序列:<a,c,c,b,a,d,d,b><a,c,c,b,a,d,d,b>
当然,这样的操作序列有可能有几个,对于上例(1,3,2,4)(1,3,2,4),<a,c,c,b,a,d,d,b><a,c,c,b,a,d,d,b>是另外一个可行的操作序列。Tom希望知道其中字典序最小的操作序列是什么。
输入格式
第一行是一个整数n。
第二行有n个用空格隔开的正整数,构成一个1−n的排列。
输出格式
共一行,如果输入的排列不是“可双栈排序排列”,输出数字00;否则输出字典序最小的操作序列,每两个操作之间用空格隔开,行尾没有空格。
输入输出样例
输入 #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
说明/提示
30%的数据满足: n≤10
50%的数据满足:n≤50
100%的数据满足:n≤1000
观察数据规模,发现这道题对时间复杂度的要求并不高,n2的算法可以轻松通过,甚至连logn n2也是可以的,再观察题干,发现这道题如果跳过模拟直接得出答案很困难,凭一己之力很难找到规律,所以最开始还是要从模拟当中找到答案。但是直接模拟也很难。让人有一种无从下手的感觉,那我们还是从特殊到一般试试。看见“双”,先想“单”,假设我们只有一个栈,怎么排序?在排序中,栈的作用到底是什么?仔细想一想,不难发现其实就是辅助交换逆序对,并且它的能力有限,对于2 3 1这样的数据是无能为力的。
那么“2 3 1”,这种数据有什么性质?它不单调!,对于没有单调性质的数据。我们会发现都不可以用单栈排序。
那么扩展到双栈呢?,这种情况下,我们就可以将1压进1号栈,2压进2号栈,3压进1号栈,再分别弹出3,1,2,就完成了排序。
那当其中有一个栈失去了单调性质,那么这个排序也就gg了。所以说,我们要判断可不可以用双栈排序,本质上就是要判断数列中存不存在a4<a1<a2<a3这一类的数据。当然我们可以暴力判O(4^n) , 也可以优化一下先预处理出后缀最小值,然后O(n^3 )判断,当然都会TLE。
那怎么办?两个栈交替存储输出,不就是一个二分图嘛?那利用二分图的思想,就能轻松在n2的时间复杂度内解决这道题了。
附上代码~
关于二分图的一些技巧就在注释里面了
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int Inf=0x3f3f3f3f;
int n,cnt=0,not_bip=0;//cnt记录操作数,目测cnt最后会等于2*n因为每个数都要进栈和出栈
int len1=0,len2=0;//记录两个栈的大小
int Wait_print=1;//我们当前该输出啥数
int dt[1010][1010];//存图
int Sm[1010];//后缀最小值
int sl[1010];//原数列
int Q[1000010];//Bfs队列
int col[1010];//每个数的颜色
int vis[1010];//Bfs visit数组
int stack1[1010];//1栈
int stack2[1010];//2栈
char Oper[2020];//操作,这个数组可以不开,大家可以在过程中直接输出,而且会更快
void Suffix_min()//预处理后缀最小值
{
Sm