二分图染色 - 双栈排序(NOIP2008提高组)

本文深入探讨了一种特殊的排序问题,即如何利用两个栈对序列进行升序排列,提出了一种有效的双栈排序算法,并详细解释了其工作原理。文章通过实例展示了算法的具体操作过程,分析了算法的正确性和效率,为理解和应用双栈排序提供了全面的指导。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

在这里插入图片描述

操作a:
如果输入序列不为空,将第一个元素压入栈 S1

操作b:
如果栈 S1 不为空,将 S1 栈顶元素弹出至输出序列

操作c:
如果输入序列不为空,将第一个元素压入栈 S2

操作d:
如果栈 S2 不为空,将 S2 栈顶元素弹出至输出序列

如果一个 1~n 的排列 P 可以通过一系列操作使得输出序列 1,2,…,(n-1),n,Tom 就称 P 是一个“可双栈排序排列”。例如(1,2,3,4)就是一个“可双栈排序排列”,而(2,3,4,1)不是。下图描述了一个将(1,3,2,4)排序的操作序列:

当然,这样的操作序列有可能多个,对于上例(1,2,3,4),是另外一个可行的操作序列。Tom 希望知道其中字典序最小的操作序列是什么。

输入格式

输入两行:
第一行是一个整数 n 。
第二行有 n 个用空格隔开的正整数,构成一个 1~n 的排列。

输出格式

输出一行,如果输入的排列不是“可双栈排序排列”,输出数字 0;否则输出字典最小的操作序列,每个操作之间用空格隔开,行尾没有空格。

样例数据 1

输入

4
1 3 2 4
输出

a b a a b b a b
样例数据 2

输入

4
2 3 4 1
输出

0
样例数据 3

输入

3
2 3 1
输出

a c a b b d
备注

【数据范围】
30% 的数据满足:n<=10
50% 的数据满足:n<=50
100% 的数据满足:n<=1000


Analysis

就算知道是二分图染色,也毫无思绪的我T_T
先手写一个贪心,过了30分
然后发现这个贪心显然错误,弃疗。

跑去翻题解,哦哦,原来要推性质
s[i]s[i]s[i]s[j]s[j]s[j]i&lt;ji&lt;ji<j)不能在同一个栈存在(不要求同时出现)的条件就是⇔ 存在一个k,使得 i < j < k 且 a [ k ] < a [ i ] < a [ j ]
感性理解一下:
因为一个数只能进出一次,k 要排在前面所以弹出 k 时 i 和 j 都在栈里,如果两者在同一个栈弹出后顺序就错误了,然后找不到除这种情况外的反例,于是就这样了。。。
我们把不能放在一个栈的数连一条边,然后二分图染色,判断是否存在解
(我们是把二分图的两个点集合看做两个栈,在同一个点集的当然不能连边)

最后注意一下输出,但好像没有卡吧


Code
#include<bits/stdc++.h>
#define in read()
#define N 1009
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
int n,s[N],mn[N],color[N];
int stk1[N<<2],tp1,stk2[N<<2],tp2;
int nxt[N*N],to[N*N],head[N],ecnt=0;
void add(int x,int y){nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;}
inline void pre_work(){
	mn[n]=s[n];
	for(int i=n-1;i>=1;--i) mn[i]=min(mn[i+1],s[i]);
	for(int i=1;i<n;++i)
		for(int j=i+1;j<=n;++j)
			if(mn[j]<s[i]&&s[i]<s[j]) add(i,j),add(j,i);
}
bool dfs(int u,int col){
	color[u]=col;
	for(int e=head[u];e;e=nxt[e]){
		int v=to[e];
		if(!color[v]) dfs(v,3-col);
		else if(color[v]==col) return 0;
	}
	return 1;
}
int main(){
	n=in;
	int i,j,num=1;
	for(i=1;i<=n;++i) s[i]=in;
	pre_work();
	for(i=1;i<=n;++i) if(!color[i]&&!dfs(i,1)) return printf("0"),0;
	for(i=1;i<=n;++i){
		if(color[i]==1) stk1[++tp1]=s[i],printf("a ");
		else stk2[++tp2]=s[i],printf("c ");
		while(num==stk1[tp1]||num==stk2[tp2]){
			if(num==stk1[tp1]){
				tp1--;printf("b");
				if(num!=n) printf(" ");
			} 
			else {
				tp2--,printf("d");
				if(num!=n) printf(" ");	
			}
			num++;
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值