题意理解
给你一个 1∼n1 \sim n1∼n 的排列 PPP,你需要用两个栈 S1 和 S2,通过四种操作:
* a:把当前输入序列的第一个元素压入 S1
* b:把 S1 的栈顶弹出并加入输出序列
* c:把当前输入序列的第一个元素压入 S2
* d:把 S2 的栈顶弹出并加入输出序列
目标:让最终的输出序列恰好是 1,2,…,n1, 2, \dots, n1,2,…,n。
如果可以做到,就输出字典序最小的操作序列;否则输出 0。
例如:
* 输入 1 3 2 4 → 输出 a b a a b b a b
* 输入 2 3 4 1 → 输出 0(不可能)
* 输入 2 3 1 → 输出 a c a b b d
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
算法思路
1. 什么时候两个数不能进入同一个栈?
这是本题的关键。假设有两个位置 i<ji < ji<j,且 Pi<PjP_i < P_jPi <Pj ,如果存在一个 k>jk > jk>j 使得 Pk<PiP_k < P_iPk <Pi ,那么 PiP_iPi 和 PjP_jPj 不能进入同一个栈。
为什么?
* 因为 PiP_iPi 必须在 PkP_kPk 之前输出(Pi<PkP_i < P_kPi <Pk ),所以 PiP_iPi 在栈里时不能压着 PkP_kPk ;
* PjP_jPj 又比 PiP_iPi 大,如果它们同栈,由于 PiP_iPi 先进栈,PjP_jPj 后进栈,PjP_jPj 会压在 PiP_iPi 上面,导致必须先弹出 PjP_jPj 才能弹出 PiP_iPi ,这就和 PiP_iPi 必须在 PkP_kPk 之前弹出矛盾了。
因此,满足上述条件的 (i,j)(i, j)(i,j) 之间连一条边,表示它们必须在不同栈。
(小钥匙🔑:代码中有我加的干扰输出,自己找哈)
2. 二分图染色
根据上面的规则建图,然后进行二分图染色。如果染色冲突(即出现奇环),说明无解,输出 0。
染色时,为了字典序最小,通常让编号小的点尽量染成颜色 0(代表第一个栈,操作 a 的字典序比 c 小)。
3. 模拟操作
染色成功后,按顺序模拟:
* 维护一个目标值 need = 1,表示下一个需要输出的数字。
* 如果 S1 栈顶等于 need,执行 b,弹出,need++。
* 否则如果 S2 栈顶等于 need,执行 d,弹出,need++。
* 否则,还有数字未入栈:
* 如果当前数字颜色为 0,执行 a,压入 S1。
* 否则执行 c,压入 S2。
* 如果既不能弹栈也不能压入,说明陷入死锁,无解。
为什么这样得到的就是字典序最小的操作序列?
因为 a 和 c 的字典序小于 b 和 d,但你不能无脑一直压栈——一旦可以弹出所需数字就必须立即弹出,否则会破坏后面的顺序。在能够弹出时立即弹出,同时压栈时优先选择字典序更小的 a(颜色 0),就能保证整体操作序列字典序最小。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
复杂度
建图 O(n2)O(n^2)O(n2),对于 n≤1000n \le 1000n≤1000 足够。染色和模拟都是 O(n)O(n)O(n)。总复杂度 O(n2)O(n^2)O(n2)。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码(某些人不看前面只看代码......)