sicily 1768 双栈拍序

来源:互联网 发布:德国禁止儿童手表知乎 编辑:程序博客网 时间:2024/05/17 22:07

不使用图表方法


我们先看单栈排序

定义:

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

单栈排序失败充要条件:

考虑对于任意两个数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;}elsep_stack[j]=1;}else{if(p_stack[j] == 1){p_stack[i]=2;}elsep_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_judgewhile(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();}




0 0
原创粉丝点击