码迷,mamicode.com
首页 > 编程语言 > 详细

noip2008 双栈排序

时间:2016-10-02 19:20:45      阅读:170      评论:0      收藏:0      [点我收藏+]

标签:

题目描述 Description

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)就是一个“可双栈排序序列”,而(2,3,4,1)不是。下图描述了一个将(1,3,2,4)排序的操作序列:<a,c,c,b,a,d,d,b>

输入描述 Input Description

输入的第一行是一个整数n。

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

输出描述 Output Description

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

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

 

  把以前不会写的noip题目补一下~其实现在我也还是不会。

  感觉这道题思路非常神……首先,贪心的选显然是错误的。然后……这道题到底该怎么做啊啊啊!

  于是到最后我还是去看题解了……

  这道题首先要证明一个结论:对于任意两个数ai和aj来说,它们不能压入同一个栈(不仅仅指同时在栈中,而是压入了同一个栈)中的充要条件是:存在一个k,使得i<j<k且ak<ai<aj

  首先证明充分性。由于ak<ai,那么当ak入栈时,aj会在ai下面,于是无解。

  然后证明必要性。这个东西比较难证,于是我们可以转而证明它的逆否命题:若对于任意的i<j<k,不满足ak<ai<aj,那么他们可以压入一个栈。

  对于不满足ak<ai<aj有两种情况:一是ak>ai,二是ai>aj

  对于第一种情况,由于ak>ai,所以在ak入栈之前ai已经可以弹出,于是可以压入同一个栈。

  对于第二种情况,显然不论ak大小如何,都可以先把ai和aj压入同一个栈,aj会先于ai弹出。

  于是该命题的逆否命题得证,所以原命题得证。

  于是,这个问题就变成了二分图的问题了。把不可以压入同一个栈的点连边,然后判断一个图不是二分图就无解,否则有解。

  然后就是字典序最小的问题了。由于字典序最小需要靠前的数尽量往第一个栈里丢,所以可以从前到后判断一下,在二分图中dfs一遍。

  于是这道题就这么愉快的解决辣!话说考场上碰到这种题我真的写得出来吗?

  小结一下:1.碰到一类毫无思路的问题时,要推一推题目中有没有什么奇妙的性质;

         2.发现自己的复杂度远过低时,一定要多检查几遍;

         3.一定要先写暴力,可以用来验证自己猜的结论。

  下面贴代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define N 1010

using namespace std;
typedef long long llg;

int n,a[N<<1],wa[N],co[N],b[N],lb;
int head[N],next[N*N],to[N*N],tt;
int s1[N],t1,s2[N],t2,now=1;
bool vis[N];

int getint(){
	int w=0;bool q=0;
	char c=getchar();
	while((c>‘9‘||c<‘0‘)&&c!=‘-‘) c=getchar();
	if(c==‘-‘) c=getchar(),q=1;
	while(c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar();
	return q?-w:w;
}

void link(int x,int y){
	to[++tt]=y;next[tt]=head[x];head[x]=tt;
	to[++tt]=x;next[tt]=head[y];head[y]=tt;
}

bool ran(int u){
	bool ww=0;
	for(int i=head[u],v;v=to[i],i;i=next[i])
		if(co[v]==co[u]) return 0;
		else if(!co[v]) co[v]=3-co[u],ww|=!ran(v);
	return !ww;
}

void dfs(int u,bool w){
	if(w) co[u]=3-co[u]; vis[u]=1;
	for(int i=head[u],v;v=to[i],i;i=next[i])
		if(!vis[v]) dfs(v,w);
}

int main(){
	File("a");
	n=getint(); wa[n+1]=2147483647;
	for(int i=1;i<=n;i++) a[i]=getint();
	for(int i=n;i>=1;i--) wa[i]=min(wa[i+1],a[i]);
	for(int i=1;i<n;i++)
		for(int j=i+1;j<n;j++)
			if(a[i]<a[j] && a[i]>wa[j+1]) link(i,j);
	for(int i=1;i<=n;i++)
		if(!co[i]){
			co[i]=1;
			if(!ran(i)){
				printf("0");
				return 0;
			}
		}
	for(int i=1;i<=n;i++)
		if(!vis[i]) dfs(i,co[i]!=1);
	for(int i=1;i<=n;i++){
		if(co[i]==1) s1[++t1]=a[i],b[++lb]=1;
		if(co[i]==2) s2[++t2]=a[i],b[++lb]=3;
		while(s1[t1]==now || s2[t2]==now){
			if(s1[t1]==now) t1--,b[++lb]=2;
			if(s2[t2]==now) t2--,b[++lb]=4;
			now++;
		}
	}
	for(int i=1;i<=lb;i++) printf("%c ",‘a‘+b[i]-1);
}

noip2008 双栈排序

标签:

原文地址:http://www.cnblogs.com/lcf-2000/p/5927832.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!