sicily 1768 双栈拍序

不使用图表方法


我们先看单栈排序

定义:

利用单栈,将一组数有序的输出

单栈排序失败充要条件:

考虑对于任意两个数p[i]和p[j],存在一个k,使得i<j<k且p[k]<p[i]<p[j]
这种情况下p[k]是一定会压在栈底,无法最先输出的,所以无法单栈排序
以下称这种i<j为单栈冲突

所以可以使用双栈排序

定义:

利用双栈,将一组数有序的输出,

双栈排序成功的情况:

1.单栈可排序(单栈排序即可)

2.单栈不可排序(如上单栈冲突)

解决方案:

p[j]放到第二个栈中,进行模拟即可


如此建立模型的意义:

这样其实简化问题为只看i,j,这两个冲突数字,不用考虑其他数字排列的可能


双栈排序不成功的情况(详见下(3)):

(1)这里注意了,上面的规律是在每个i的情况下,对满足 i < j < k 的每一个j和n都是要进行考虑的

     这个也可以用二分图的思想来思考(这个如果思考明白,那双栈就算是知道需要怎么做了)

eg:3 5 2 4 1

3 5 2要比较因为2要在3前面出去,但5会阻挡单栈排序

3 4 1也要比较,因为1也要在3之前出去,但4会阻挡单栈排序

不是说,只考虑3 5 2这一组就可以

而是满足3 < j < k的都要进行考虑


(2)在判断数对(i,j)是否满足 单栈不可排序 时,枚举检查是否存在k的时间复杂度是O(n),则总的时间复杂度是O(n^3),对于n=1000是太大了.这原因在于过多得枚举了k,我们可以用动态规划把枚举k变为O(1)的算法:求i-n的当前最小值

eg:3 5 2 6 4 1

因为1在最后,所以无论怎么枚举,都要让1先出去

所以存在一个k,使得i<j<k且p[k]<p[i]<p[j],这里的p[k],一直都等效是1,所以条件变为

所以存在一个k,使得i<j<k,且p_min[k]<p[i]<p[j] 


(3)因为考虑用图不好理解,所以使用数组栈1,栈2,记录每个数在哪个栈中

     用 i<j<k,且p_min[k]<p[i]<p[j] 的冲突条件扫描

在搜索时会有三种情况

一.i,j都没有分配栈

分配一个为栈1,一个为栈2 

二.i,j有一个分配了栈

把无栈得那个放到另一个栈中

三.i,j都有栈,则分析栈的情况

1.不是一个栈,满足单栈冲突,但是双栈可以解决

2.是一个栈,则证明前面已有的冲突要求i,j都要放到一个栈中

            则导致当前冲突无法解决,所有双栈排序不可行 


(4)构造正确的数据输出序列

为了使测试的输出为字典序,需要用到贪心法

我们先得出正确的数据输出序列,然后每次压入一个数据

然后看当前两个栈中有没有符合正确输出序列的值,有的话就可以直接压出,没有则依照规则继续压入

这样不用考虑顺序问题,简单明了

#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <string.h>
using namespace std;

const int MAXE=1000+10;

int case_num;//输入的数据个数 
int p[MAXE];//储存输入数据
int p_judge[MAXE];//最后使用贪心法输出时进行判断 
int p_min[MAXE];//储存当前下标及之后序列中的的最小值 
int p_stack[MAXE];//存储数据属于哪个栈 

bool devide_stack(int *p_stack);
void greedy_sort(); 

int main(){
	while(cin >> case_num){
		if(case_num==0){
			cout << 0 << endl;
			continue;
		}
		memset(p,0,sizeof(p));
		memset(p_judge,0,sizeof(p_judge));
		memset(p_min,0,sizeof(p_min));
		memset(p_stack,0,sizeof(p_stack));
		for(int i = 0; i < case_num ;i++){
			cin>>p[i];
			p_min[i]=p[i];
			p_judge[i]=p[i];
		}
//		用排序得出一个使用贪心法输出时进行判断的序列 ,既正确的输出数据序列 
		sort(p_judge,p_judge+case_num); 
// 		为了满足分析时提到的条件,求i-n的当前最小值,放到p_min中 
		p_min[case_num]=p[case_num-1];
		for(int i = case_num-1; i >= 0; i--){
			p_min[i]=min( p_min[i+1], p[i] ); 
		} 
//		判断双栈是否可行,并且给数据分栈 
		bool ok=devide_stack(p_stack);
//		现在已经分成了两个栈,1和0代表一个栈,2代表一个栈
//		接下来就用贪心法输出操作步骤就可以
		if(ok)
		greedy_sort();
	}
	return 0;
}

bool devide_stack(int *p_stack){
	for(int i = 0; i < case_num-1 ;i++){
		for(int j = i+1; j < case_num; j++){
//			介绍中所给的判断双栈条件 
			if( (p[i] < p[j])&&(p_min[j+1] < p[i]) ){
//			都未分栈,初始化栈1,栈2 
				if( (!p_stack[i]) && (!p_stack[j]) ){
					p_stack[i]=1;
					p_stack[j]=2;
				}
//			都分栈,这里就是判断是否双栈不可行的地方 
				else if( p_stack[i]&&p_stack[j] ){
					if(p_stack[i]==p_stack[j]){
						cout << 0 << endl;
						return false;
					}
				}
//			只分了一个栈,给另一个分栈即可	
				else{
					if( p_stack[i] ){
						if(p_stack[i] == 1){
							p_stack[j]=2;	
						}
						else
							p_stack[j]=1;

					}else{
						if(p_stack[j] == 1){
							p_stack[i]=2;	
						}
						else
							p_stack[i]=1;
					}
				}
			}
		}
	}
	return true;
}

void greedy_sort(){
	stack<int> s1;//模拟第一个栈 
	stack<int> s2;//模拟第二个栈 
	queue<char> s3;//用队列的操作来输出 
	int count=0;// 
	for(int i = 0; i < case_num; i++){
//		根据栈1或栈2正常输入 
		if(p_stack[i] != 2){
			s3.push('a'); 
			s1.push( p[i] ); 
		}
		else{
			s3.push('c');
			s2.push( p[i] );
		}
//		把所有能输出的进行输出,这样可以不用考虑是先压入还是先输出 
//		这个利用正确的输出数据序列 p_judge
		while(1){
			if(!s1.empty() && p_judge[count] == s1.top() ){
				s3.push('b');
				s1.pop(); 
				count++;
			}
			else if(!s2.empty() && p_judge[count] == s2.top() ){
				s3.push('d');
				s2.pop();
				count++;
			}
			else break;
		}
	} 
	//满足输出格式 
	while(s3.size() != 1){
		cout << s3.front() << " " ;
		s3.pop(); 
	} 
	cout << s3.front() << endl;
	s3.pop();

}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值