双栈排序
首先有一个比较错误的贪心思路,就是假设我们前面的操作是最优方案,对于当前的元素,能放第一个栈就放第一个栈;如果不行那么第一个栈的栈顶元素小于当前元素,此时如果能弹出这个栈顶元素就弹出这个栈顶元素,然后重新从最开始的位置开始考虑;否则尝试将当前元素放入第二个栈;如果不行则第二个栈的栈顶元素小于当前元素,直接将第二个栈的栈顶元素弹出,然后重新从最开始的位置开始考虑
这个贪心的方法就有可能导致一个本来可以排序的序列,不能通过这个方法成功排序
那么我们怎么想呢?
假设我们已经对每一个数分到的栈的编号确定了,假设我们前面的操作方案已经是最优的了,此时两个栈的元素一定都是单调递减的,对于当前元素,如果他能够放入其想放入的栈,那么就直接放入;否则其想放入的栈的栈顶元素一定小于这个元素,所以我们要将这个元素弹出,设栈顶元素为
从上面这个伪过程可以看出,对于两个下标
实际上这个结论是充要的。必要性很容易证明,下面证明充分性,也就是证明如果
我们证明其逆否命题:如果
我们考虑已经分好了的两个组,其中每个组中的任意二元组都不满足条件(注意
此时每个元素进入栈的编号都已经确定,我们依次模拟。假设前面的模拟都合法(也就是说此时两个栈的元素都单调递减),考虑当前元素,不妨设其要进入第一个栈,如果栈顶元素大于当前元素,那么直接放入,满足栈的元素单调递减而且此次操作合法;否则的话,考虑弹出第一个栈的栈顶元素,设栈顶元素为
综上,我们对任意的不满足条件的两个组,我们一定能够找到一种合法的方案
所以这个时候,我们对不可能在一个组的
现在我们要求字典序最小,假设我们前面已经是最优的操作方案了,那么对于当前元素,
1.如果这个元素已经被确定放在哪个栈中了
1).如果是第一个栈,那么我们能放入则放入(根据我们上面的证明,此时放入一定可以是解,而且一定是字典序最小的解);否则的话我们考虑弹出,此时的操作顺序是固定的,最后将这个元素放入第一个栈就好了;
2).如果是第二个栈
①.如果当前元素小于栈顶元素,我们考虑先将第一个栈能弹出的元素弹出,然后再放入当前元素,此时一定可以找到一个解(因为此时还是满足两个栈的元素单调递减),而且字典序最小
②.如果当前元素大于栈顶元素,我们考虑将栈顶元素弹出,此时的操作顺序在到达①之前都是固定的,之后再按①考虑就好了
2.如果这个元素没有被确定放入哪个栈中
1).如果这个元素比第一个栈的元素小,显然直接放入,字典序最小且一定有解
2).如果这个元素比第一个栈的栈顶元素大,那么也不会将这个元素放在第二个栈中,这是因为如果能放入第二个栈中,就说明第二个栈的栈顶元素大于当前元素,而当前元素大于第一个栈的栈顶元素,从而第一个栈的栈顶元素此时可以弹出,那么我们宁愿弹出(此时字典序最小而且仍然能找到解,因为两个栈的元素仍然单调递减),如果都不能放入第二个栈中,那么当前操作就是固定的(要么弹出第一个栈,要么弹出第二个栈),不用考虑了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构