Vijos1605 NOIP2008 提高组T4 双栈排序 BFS

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - Vijos1605


题意概括

  有1个1~n的排列,有2个栈,现在通过以下操作,使得出栈序列有序。

  操作a 当前元素入栈<S1>

  操作b 弹出S1栈顶元素

  操作c 当前元素入栈<S2>

  操作d 弹出S2栈顶元素

  如果无法使得出栈序列有序,那么输出0.

  否则输出满足条件的字典序最小的操作序列。


题解

  首先我们可以证明,任意时刻,任意一个栈中的元素,一定满足自底向上呈降序。

  如果不呈降序呢?

  那么会出现大的先出栈,小的后出栈的情况,所以一定不行。

  我们考虑2个元素a[i],a[j],(i<j)。

  可以得到信息:

  如果a[i]<a[j]那么a[i]先出栈

  否则 a[j]先出栈

  然后好像没有其他信息了。

  问题很严峻。

  于是我们考虑3个元素a[i],a[j],a[k](i<j<k)

  考虑所有情况。

  如果a[i]<a[j]<a[k],那么a[i],a[j]可能在同一个栈中。

  同样,

  如果a[i]<a[k]<a[j],那么a[i],a[j]可能在同一个栈中。

  如果a[j]<a[i]<a[k],那么a[i],a[j]可能在同一个栈中。

  如果a[j]<a[k]<a[i],那么a[i],a[j]可能在同一个栈中。

  如果a[k]<a[j]<a[i],那么a[i],a[j]可能在同一个栈中。(这5中情况都可以有具体操作次序,注意是“可能”)

  但是,

  如果a[k]<a[i]<a[j],那么a[i],a[j]一定不可能在同一个栈中。

  为什么?

  首先a[i]进栈。

  因为a[k]<a[i],所以在a[k]出栈之前,a[i]不能出栈。

  然而因为a[i]<a[j],如果a[i]与a[j]放在同一个栈中,那么会出现题解第一句中表述的不合法情况。

  因此a[i],a[j]一定不会在同一个栈中。

  于是我们可以先确定i,j,然后大力枚举k,按照不在同一个栈中的关系建立无向图。相邻的节点一定不会在同一个栈中。这个操作的复杂度为n^3。

  而实际上,我们可以先预处理一个后缀最小值,那么大力枚举k的时间复杂度就被压缩掉了。

  优化之后,这个操作的复杂度为n^2。

  建图之后,就是匹配栈。

  黑白染色即可。(假设栈1为白色(0),栈2为黑色(1))

  又由于题目要字典序最小的,所以我们尽量给编号小的节点染成白色就可以了。

  于是我们大力跑一遍广搜。

  当然染色的过程中会出现相邻节点颜色相同的情况,那么就是无解,直接输出0。

  广搜完了之后,每一个数字安排的栈位置都已经弄好了。

  然后就是模拟了。

  模拟的时候,还是要注意字典序最小的问题。

  注意按照之前匹配的一定有解的。

  模拟具体过程:

  我们弄一个计数器cnt,表示当前出栈到了哪一个数字。

  对于每次压入元素前,如果栈1的栈顶元素是cnt+1,那么要先将栈1出栈。

  因为,如果当前元素匹配的是栈2,那么自然就是栈1出栈优先。

  如果是栈1,那么元素值一定会比cnt+1要大,不满足最前面的那一条。

  然后,注意对于同一个栈,入栈的优先级一定比出栈高。

  所以,我们尽量提前入栈。

  但是入栈是有条件的,要维持序列降序。

  那么就要先出栈,直到满足降序为止。

  出栈也是有规矩的。要满足cnt+1,所以是两个栈乱序出栈的。

  我的程序貌似有点小问题,但是数据水,过去了……

  具体:

  如果匹配栈2,那么出栈操作之后,满足栈2降序之后,其实有字典序更小的方案。

  因为栈2入栈,是c,而栈1出栈是b,所以栈1先出栈是赚的。但是我忘记判这个情况了。

  然后居然……

  至此,rzO膜拜大佬出据人Orz


代码

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <queue>
using namespace std;
const int N=1000+5,Inf=100000;
struct Stack{
	int v[N],t;
	void clear(){
		t=0;
	}
	void push(int x){
		v[++t]=x;
	}
	void pop(){
		t--;
	}
	bool empty(){
		return t==0;
	}
	int top(){
		if (t==0)
			return Inf;
		return v[t];
	}
}s1,s2;
int n,a[N],Min[N],match[N],vis[N],cnt;
bool g[N][N];
queue <int> q;
int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	Min[n]=10000;
	for (int i=n-1;i>=1;i--)
		Min[i]=min(Min[i+1],a[i+1]);
	memset(g,0,sizeof g);
	for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++)
			if (a[i]<a[j]&&Min[j]<a[i])
				g[i][j]=g[j][i]=1;
	memset(vis,0,sizeof vis);
	for (int i=1;i<=n;i++){
		if (vis[i])
			continue;
		while (!q.empty())
			q.pop();
		vis[i]=1,match[i]=0;
		q.push(i);
		while (!q.empty()){
			int x=q.front();
			q.pop();
			for (int j=1;j<=n;j++){
				if (!g[x][j])
					continue;
				if (!vis[j]){
					match[j]=match[x]^1;
					q.push(j);
					vis[j]=1;
				}
				else if (match[j]==match[x]){
					printf("0");
					fclose(stdin);fclose(stdout);
					return 0;
				}
			}
		}
	}
	cnt=0;
	s1.clear(),s2.clear();
	for (int i=1;i<=n;i++){
		while (s1.top()==cnt+1){
			printf("b ");
			s1.pop();
			cnt++;
		}
		if (match[i]==0){
			while (a[i]>s1.top()&&(s1.top()==cnt+1||s2.top()==cnt+1))
				if (s1.top()==cnt+1){
					printf("b ");
					s1.pop();
					cnt++;
				}
				else {
					printf("d ");
					s2.pop();
					cnt++;
				}
			s1.push(a[i]);
			printf("a ");
		}
		if (match[i]==1){
			while (a[i]>s2.top()&&(s1.top()==cnt+1||s2.top()==cnt+1))
				if (s1.top()==cnt+1){
					printf("b ");
					s1.pop();
					cnt++;
				}
				else {
					printf("d ");
					s2.pop();
					cnt++;
				}
			s2.push(a[i]);
			printf("c ");
		}
	}
	while (!s1.empty()||!s2.empty())
		if (s1.top()<s2.top()){
			printf("b ");
			s1.pop();
		}
		else {
			printf("d ");
			s2.pop();
		}
	return 0;
}

  

时间: 2024-10-13 23:40:48

Vijos1605 NOIP2008 提高组T4 双栈排序 BFS的相关文章

2017.2.18[codevs1170]NOIP2008提高组复赛T4双栈排序

体面不贴 这题一开始卡了我好久--策了好久贪心都判断不了无解情况-- 直到看了题解才发现自己有多傻逼-- 传送门:http://blog.csdn.net/kqzxcmh/article/details/9566813 题解写的很清楚这里就不赘述了. 两次AC,还行吧. 关键是我太蒟蒻-- 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #in

BZOJ 2080: [Poi2010]Railway 双栈排序

2080: [Poi2010]Railway Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 140  Solved: 35[Submit][Status][Discuss] Description 一个铁路包含两个侧线1和2,右边由A进入,左边由B出去(看下面的图片) 有n个车厢在通道A上,编号为1到n,它们被安排按照要求的顺序(a1,a2,a3,a4....an)进入侧线,进去还要出来,它们要按照编号顺序(1,2,3,4,5....n)从通道B

NOIP2008 双栈排序 染色+模拟

挺不错的一道题,首先可以知道若存在形如 k<i<j 但 a[k]<a[i]<a[j]这样的,那么i,j一定不能(从始至终不能)进入同一个栈 例如 2 3 1,若2 3进入同一个栈,那么1再进栈然后马上出栈,这时候,2没有办法在3之前出来. 所以对于这样的i,j我们连一条边,然后dfs染色,若染色中发现相邻点颜色相同,则无解,否则我们按照1,2,1,2的顺序染色. 确定了每一个数属于哪个栈后,用2个stack模拟一下就好了. #include <iostream> #in

NOIP2008 双栈排序

题目描述 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,3,2,4)就是一个“可

Noip2008双栈排序

[问题描述] 用两个栈使一个1...n的排列变得有序.一共有四个操作: A.stack1.push() 读入一个放入栈一 B.stack1.pop() 弹出栈一放入输出序列 C.stack2.push() 读入一个放入栈二 D.stack2.pop() 弹出栈二放入输出序列 给你一个初始的排列,求一个字典序最小的操作序列使得变得有序,若没有满足条件的操作序列,输出'0'. Sample.in                                Sample.out 4     1 3

Noip2008提高组总结

Noip2008前三题是基础题,仔细一些都是可以AC的,第四题的证明很巧妙,但是看懂后代码其实很简单,感觉在这些大家都不屑去做的简单题中又学到了不少,四道题代码基本都是十几二十行就够了,渐渐感觉到,比代码和算法更重要的是思想与建模,觉得下阶段应该多注意培养自己的建模能力. T1:火柴棒等式 最简单的模拟题,首先记录下每种数字需要的火柴棒数,最后枚举验证即可. + ? 1 2 3 4 5 6 7 8 9 10 11 #include <cstdio> int main(){     int n,

双栈排序(codevs 1170)题解

[问题描述] 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,3,2,4)就是一个

二分图 and code1170 双栈排序

6.6二分图 二分图是这样一个图: 有两顶点集且图中每条边的的两个顶点分别位于两个顶点集中,每个顶点集中没有边直接相连接. 无向图G为二分图的充分必要条件是,G至少有两个顶点,且其所有回路的长度均为偶数. 判断二分图的常见方法是染色法: 开始对任意一未染色的顶点染色,之后判断其相邻的顶点中,若未染色则将其染上和相邻顶点不同的颜色, 若已经染色且颜色和相邻顶点的颜色相同则说明不是二分图,若颜色不同则继续判断,bfs和dfs都可以. 易知:任何无回路的的图均是二分图. 代码: bool Color(

AC日记——双栈排序 洛谷 P1155

双栈排序 思路: 二分图染+模拟: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 1005 #define maxm 2000005 int n,head[maxn],E[maxm],V[maxm],cnt,col[maxn]; int minn[maxn],ai[maxn],sta1[maxn],sta2[maxn],top1,top2; bool if_[maxn][maxn]; inline void in(