首页 > 其他分享 >双栈排序——二分图+模拟

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

时间:2022-11-30 22:57:15浏览次数:57  
标签:二分 int s2 样例 双栈 序列 操作 排序

二分图建模-双栈排序

题目描述

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

操作 \(a\):将第一个元素压入栈 \(S_1\)。

操作 \(b\):将 \(S_1\) 栈顶元素弹出至输出序列。

操作 \(c\):将第一个元素压入栈 \(S_2\)。

操作 \(d\):将 \(S_2\) 栈顶元素弹出至输出序列。

如果一个 \(1\sim n\) 的排列 \(P\) 可以通过一系列合法操作使得输出序列为 \((1,2,\cdots,n-1,n)\),Tom 就称 \(P\) 是一个“可双栈排序排列”。例如 \((1,3,2,4)\) 就是一个“可双栈排序序列”,而 \((2,3,4,1)\) 不是。下图描述了一个将 \((1,3,2,4)\) 排序的操作序列:\(\texttt {a,c,c,b,a,d,d,b}\)。

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

输入格式

第一行是一个整数 \(n\)。

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

输出格式

共一行,如果输入的排列不是“可双栈排序排列”,输出 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\%\) 的数据满足:\(n\le10\)。

\(50\%\) 的数据满足:\(n\le50\)。

\(100\%\) 的数据满足:\(n\le1000\)。

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

分析

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

\(\exists i,j,k(i<j<k),a_k<a_i<a_j\)

证明显然

那么由这个性质,不难发现\((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;
}

标签:二分,int,s2,样例,双栈,序列,操作,排序
From: https://www.cnblogs.com/oierpyt/p/16940074.html

相关文章

  • 力扣 leetcode 153. 寻找旋转排序数组中的最小值
    问题描述已知一个长度为n的数组,预先按照升序排列,经由1到n次旋转后,得到输入数组。例如,原数组nums=[0,1,2,4,5,6,7]在变化后可能得到:若旋转4次,则可以得到[4......
  • 力扣 leetcode 33. 搜索旋转排序数组
    问题描述整数数组nums按升序排列,数组中的值互不相同。在传递给函数之前,nums在预先未知的某个下标k(0<=k<nums.length)上进行了旋转,使数组变为[nums[k],nums[k......
  • 洛谷 P1387 最大正方形(前缀和,二分)
    题目分析当一个边长为x的正方形不包含0时,这个正方形内的二维前缀和为x*x,题目想求满足条件的最大的正方形的边长假如最大的正方形的边长为ans,那么凡是边长大于ans的正方形......
  • DQL-分页和排序-2022-11-30
    分页和排序--排序升序ASC 降序DESC --语法ORDERBY   SELECTs.`studentno`,studentname,`subjectno`,`studentresult`FROMstudentASsLEFTJOINresult......
  • C++数据结构和算法:排序算法
    为了便于测试,先写一个生成随机数组的方法。1pair<int*,int>GenerateRandomArray(intmaxSize,intmaxValue,intminValue)2{3//随机数组长度4cons......
  • java排序算法
     一.冒泡排序特点:实现简单,无额外空间消耗,速度较慢,适合数据较少的场景,复杂度为O(N^2)思路:每一轮比较都从头开始,然后两两比较,如果左值比右值大,则交换位置,每一......
  • 【文章精选集锦】Java 内存模型与 volatile :happens-before,重排序,内存屏障
    【文章精选集锦】Java内存模型与volatile:happens-before,重排序,内存屏障Kotlin开发者社区 3天前很多时候,千言万语不如一张图:  停停停,发错了,看下面的JVM内存模型图: ......
  • 二分浅入
    二分前言仅看要点不是很能理解,主要是看示例要点总结复习用要点关注范围,需要的是\([left,right]\)还是\([left,right)\)定义最后\(l\)和\(r\)落点位置是什么根......
  • 二叉排序树的创建与使用
    根据大家的意见,从这个题目开始,以后都会简单注释,这样更方便大家阅读,如果还有什么不懂的地方,可以留言!描述二叉排序树的定义是:或者是一棵空树,或者是具有下列性质的二叉树:(1)若它......
  • 奖学金 qsort函数多重排序
    奖学金时间限制(普通/Java):1000MS/3000MS         运行内存限制:65536KByte总提交:70           测试通过:31描述p{margin-bottom:0......