双栈排序——二分图+模拟

二分图建模-双栈排序

题目描述

Tom 最近在研究一个有趣的排序问题。如图所示,通过 2 个栈 S1S2,Tom 希望借助以下 4 种操作实现将输入序列升序排序。

操作 a:将第一个元素压入栈 S1

操作 b:将 S1 栈顶元素弹出至输出序列。

操作 c:将第一个元素压入栈 S2

操作 d:将 S2 栈顶元素弹出至输出序列。

如果一个 1n 的排列 P 可以通过一系列合法操作使得输出序列为 (1,2,,n1,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,b,a,a,b,b,a,b 是另外一个可行的操作序列。Tom 希望知道其中字典序最小的操作序列是什么。

输入格式

第一行是一个整数 n

第二行有 n 个用空格隔开的正整数,构成一个 1n 的排列。

输出格式

共一行,如果输入的排列不是“可双栈排序排列”,输出 0

否则输出字典序最小的操作序列,每两个操作之间用空格隔开,行尾没有空格。

样例 #1

样例输入 #1

4
1 3 2 4

样例输出 #1

a b a a b b a b

样例 #2

样例输入 #2

4
2 3 4 1

样例输出 #2

0

样例 #3

样例输入 #3

3
2 3 1

样例输出 #3

a c a b b d

提示

30% 的数据满足:n10

50% 的数据满足:n50

100% 的数据满足:n1000

2021.06.17 加强 by SSerxhs。hack 数据单独分为一个 subtask 防止混淆。

分析

首先来考虑单栈排序,不难发现,若使单栈排序无解,当且仅当

i,j,k(i<j<k)ak<ai<aj

证明显然

那么由这个性质,不难发现(i,j)构成顺序对,(i,k),(j,k)构成逆序对,那么k就可以简单的处理,做一个后缀最小值即可判断i,j能否满足条件

那么我们将不能共存的(i,j)连一条无向边,再做一个二分图,就可以判定是否可行了(在二分图的时候优先将编号小的节点划到S1)并且划定集合

那么问题就变成了对两个栈进行单栈排序,模拟即可

需要注意的是,两个栈一定都单调的,于是为了保证字典序,我们可以在第二个栈必须出栈的时候再出

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<stack>
using namespace std;
#define N 50500
#define M 3000500
int tot,a[N],cnt,now,c[N],head[N],ver[N],nxt[N],f[N],n,m;
stack<int>s1,s2;
void add(int u,int v){
	nxt[++tot]=head[u],ver[head[u]=tot]=v;
}
void dfs(int u,int num){
	c[u]=num;
	for(int i=head[u];i;i=nxt[i]){
		int v=ver[i];
		if(!c[v])dfs(v,num==1?2:1);
		if(c[v]==c[u]){
			puts("0");
			exit(0); 
		}
	}
}
void init(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	f[n]=a[n];
	for(int i=n-1;i>0;i--)f[i]=min(f[i+1],a[i]);
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			if(a[i]<a[j]&&f[j]<a[i])
				add(i,j),add(j,i);
	for(int i=1;i<=n;i++)if(!c[i])dfs(i,1);
}
void solve(int x,int id){
	int flag=0;
	while(!flag){
		flag=1;
		while(!s1.empty()&&s1.top()==now+1){
			printf("b ");
			now++;
			flag=0;
			s1.pop();
		}
		if(x!=-1&&c[id]==1&&(s1.empty()||s1.top()>x)){	
			s1.push(x);
			printf("a ");
			break;
		}
		if(x!=-1&&c[id]==2&&(s2.empty()||s2.top()>x)){
			s2.push(x);
			printf("c ");
		}
		while(!s2.empty()&&s2.top()==now+1){
			printf("d ");
			now++;
			flag=0;
			s2.pop();
		}
	}
	if(x==-1)return ;
	
}
int main(){
	init();
	for(int i=1;i<=n;i++)solve(a[i],i);
	solve(-1,-1);
	return 0;
}

posted @   spdarkle  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示