sicily双栈排序
来源:互联网 发布:手机宣传视频制作软件 编辑:程序博客网 时间:2024/05/16 07:35
- 题目
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>
当然,这样的操作序列有可能有几个,对于上例(1,3,2,4),<a,c,c,b,a,d,d,b>是另外一个可行的操作序列。Tom希望知道其中字典序最小的操作序列是什么。
Input
输入有多组Case,每个Case第一行是一个整数n(n<=1000)。
第二行有n个用空格隔开的正整数,构成一个1~n的排列。
Output
每组Case输出一行,如果输入的排列不是“可双栈排序排列”,输出数字0;否则输出字典序最小的操作序列,每两个操作之间用空格隔开,行尾没有空格。
41 3 2 442 3 4 1
a b a a b b a b
- 解析
1.先考虑单栈情况:
定理:考虑对于任意两个数q[i]和q[j],它们不能压入同一个栈中的充要条件: 存在一个k,使得i<j<k且q[k]<q[i]<q[j]。
证明:
充分性:即如果满足上述条件,那么q[i]和q[j]一定不能压入同一个栈。
反证法:假设这两个数压入了同一个栈,那么压入q[k],因为q[k]比q[i]和q[j]都小,所以很显然,当q[k]没有被弹出的时候,另两个数也都不能被弹出(否则输出序列的数字顺序就不是1,2,3,…,n了)。而之后,无论其它的数字在什么时候被弹出,q[j]总是会在q[i]之前弹出,而q[j]>q[i],这显然是不正确的.
必要性:如果两个数不可以压入同一个栈,那么它们一定满足上述条件。证明逆否命题:也就是"如果不满足上述条件,那么这两个数一定可以压入同一个栈。”不满足上述条件有两种情况:情况1:对于任意i<j<k且q[i]<q[j],q[k]>q[i];(即对任意三个数,最小的总是在最前面)情况2:对于任意i<j,q[i]>q[j]。 第一种情况:在q[k]被压入栈的时候,q[i]已经被弹出栈。那么,q[k]不会对q[j]产生任何影响(这里可能有点乱,因为看起来,q[j]<q[k]的时候是会有影响的,但实际上,这还需要另一个数r,满足j<k<r且 q[r]<q[j]<q[k],也就是证明充分性的时候所说的情况。而事实上我们现在并不考虑这个r,所以说q[k]对q[j]没有影响)。第二种情况:可以发现这其实就是一个降序序列,所以所有数字都可以压入同一个栈。这样,原命题的逆否命题得证,所以原命题得证。
2. 双栈情况:
2.1 判断是否有解和任意两个数能否压入同一个栈
(1) 对任意两个数q[i]和q[j],若存在一个k,使得i<j<k且q[k]<q[i]<q[j],则这两个数分别入s1栈和s2栈
(2) 将s1栈和s2栈中的数字分成两个顶点子集,同一顶点子集内不会出现任何连边,即不能压入同一个栈的所有数字被分到了两个栈中。任意两个不在同一栈的数字间连边。此时我们只考虑检查是否有解,所以只要花O(n)时间检查这个图是不是二分图,就可以得知是否有解了。
*(二分图是一种特殊类型的图:图中的顶点集被划分成X与Y两个子集,图中每条边的两个端点一定是一个属于X而另一个属于Y。二分图的匹配是求边的一个子集,该子集中的任意两条边都没有公共的端点。)
2.2 解题步骤:
(1) 检查数列中的数字是否满足进入同一个栈的条件,如果不满足则将边连上,构造二分图。
(2) 用dfs染色,把二分图染成1和2两种颜色,使得染色为1的结点被压入s1栈,染色为2结点被压入s2栈。具体:每次选取一个未染色的编号最小的结点,将它染色为1,并从它开始dfs染色,直到所有结点都被染色为止。这样,我们就得到了每个结点应该压入哪个栈中。注意dfs结束之后未被染色的数字是可以被放入同一个栈的,所以优先染色为1。如果发现某一个数字两次要染不同的颜色,则为不能输出。
(3) 模拟出栈操作。
3.特殊情况与注意点:
3.1 为了判断是否满足同一个栈的条件,需要对数组进行预先处理,得到一个记录每一位数字之后最小数字的数列。
3.2 模拟输出的时候,考虑这样一种情况:此时将栈1排空了,可以压入数字,也可以继续输出栈2的数字。如果模拟的时候直接顺序排列a, b, c, d四种操作,则b结束后,因为预先染色不能压入c,则进行d操作了。这是不对的,因为需要先压入栈1,再排出栈2,顺序应该是a d而不是d a。从这个角度来说,找到的很多代码其实是错误的。需要增加一个循环元素,如果排空栈1之后可以压入则回到循环顶部先压入s1在去输出栈2的数字。
- 代码
#include <iostream>#include <cmath>#include <stack>#include <string>using namespace std;// globalbool edges[1005][1005]; // if there is edges between pos i and pos j numint color[1005]; // color of each num, 0 initial, 1 color A, 2 color Bbool can_output; // if can be sort// put color on num using dfsvoid dfs(int keyPos, int color_, int n) { color[keyPos] = color_; for (int i = 0; i < n; i++) { if (edges[keyPos][i]) { if (color[i] == color_) { can_output = false; break; } else if (!color[i]) { dfs(i, 3 - color_, n); } } if (edges[i][keyPos]) { if (color[i] == color_) { can_output = false; break; } else if (!color[i]) { dfs(i, 3 - color_, n); } } }}int main () { int n; int num[1005]; int postMin[1005]; // smallest num behind each pos while (cin >> n) { // initialize for (int i = 0; i < 1005; i++) { color[i] = 0; for (int j = 0; j < 1005; j++) { edges[i][j] = false; } } can_output = true; // input for (int i = 0; i < n; i++) { cin >> num[i]; } // compute postMin for (int i = n - 1; i >= 0; i--) { if (i == n - 1) { postMin[i] = num[i]; } else { postMin[i] = min(num[i], postMin[i + 1]); } } // make edges //int count = 0; for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { if (num[i] < num[j] && postMin[j] < num[i]) { // cannot be put into same stack; edges[i][j] = edges[j][i] = true; //count++; } } } //cout << "Edges: " << count << endl; // color the num for (int i = 0; i < n; i++) { if (!color[i]) { dfs(i, 1, n); } } // output // cout << "Start output" << endl; stack<int> s1, s2; string order; if (can_output) { // simulate output // cout << "Start simulation" << endl; int output_num = 1; // the num to be output int pos = 0; // the pos of the numbers // no edge between two num means they can be put into one stack while (output_num <= n) { if (color[pos] == 1) { order += "a"; s1.push(num[pos++]); // cout << "Push: " << num[pos - 1] << " a" << endl; } while (!s1.empty() && s1.top() == output_num) { // cout << "Pop: " << s1.top() << " b" << endl; s1.pop(); order += "b"; output_num++; } if (color[pos] == 1 && (s1.empty() || s1.top() > num[pos])) { continue; } while (!s2.empty() && s2.top() == output_num) { // cout << "Pop: " << s2.top() << " d" << endl; s2.pop(); order += "d"; output_num++; } if (color[pos] == 2) { order += "c"; s2.push(num[pos++]); // cout << "Push: " << num[pos - 1] << " c" << endl; } } for (int i = 0; i < order.length(); i++) { if (i) { cout << " "; } cout << order[i]; } cout << endl; } else { cout << "0" << endl; } while (!s1.empty()) { s1.pop(); } while (!s2.empty()) { s2.pop(); } order.clear(); } return 0;}
- 代码
#include <iostream>#include <cmath>#include <stack>#include <string>using namespace std;// globalbool edges[1005][1005]; // if there is edges between pos i and pos j numint color[1005]; // color of each num, 0 initial, 1 color A, 2 color Bbool can_output; // if can be sort// put color on num using dfsvoid dfs(int keyPos, int color_, int n) { color[keyPos] = color_; for (int i = 0; i < n; i++) { if (edges[keyPos][i]) { if (color[i] == color_) { can_output = false; break; } else if (!color[i]) { dfs(i, 3 - color_, n); } } if (edges[i][keyPos]) { if (color[i] == color_) { can_output = false; break; } else if (!color[i]) { dfs(i, 3 - color_, n); } } }}int main () { int n; int num[1005]; int postMin[1005]; // smallest num behind each pos while (cin >> n) { // initialize for (int i = 0; i < 1005; i++) { color[i] = 0; for (int j = 0; j < 1005; j++) { edges[i][j] = false; } } can_output = true; // input for (int i = 0; i < n; i++) { cin >> num[i]; } // compute postMin for (int i = n - 1; i >= 0; i--) { if (i == n - 1) { postMin[i] = num[i]; } else { postMin[i] = min(num[i], postMin[i + 1]); } } // make edges //int count = 0; for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { if (num[i] < num[j] && postMin[j] < num[i]) { // cannot be put into same stack; edges[i][j] = edges[j][i] = true; //count++; } } } //cout << "Edges: " << count << endl; // color the num for (int i = 0; i < n; i++) { if (!color[i]) { dfs(i, 1, n); } } // output // cout << "Start output" << endl; stack<int> s1, s2; string order; if (can_output) { // simulate output // cout << "Start simulation" << endl; int output_num = 1; // the num to be output int pos = 0; // the pos of the numbers // no edge between two num means they can be put into one stack while (output_num <= n) { if (color[pos] == 1) { order += "a"; s1.push(num[pos++]); // cout << "Push: " << num[pos - 1] << " a" << endl; } while (!s1.empty() && s1.top() == output_num) { // cout << "Pop: " << s1.top() << " b" << endl; s1.pop(); order += "b"; output_num++; } if (color[pos] == 1 && (s1.empty() || s1.top() > num[pos])) { continue; } while (!s2.empty() && s2.top() == output_num) { // cout << "Pop: " << s2.top() << " d" << endl; s2.pop(); order += "d"; output_num++; } if (color[pos] == 2) { order += "c"; s2.push(num[pos++]); // cout << "Push: " << num[pos - 1] << " c" << endl; } } for (int i = 0; i < order.length(); i++) { if (i) { cout << " "; } cout << order[i]; } cout << endl; } else { cout << "0" << endl; } while (!s1.empty()) { s1.pop(); } while (!s2.empty()) { s2.pop(); } order.clear(); } return 0;}
- 解题过程与总结
1. 前前后后花了一天多的时间来写。先是自己写,之后花了很多时间调试都是WA。没有想到数学原理,直接考虑如果栈顶数字大于现在的数字就可以压入,否则不能压入;优先压入栈1;如果两个栈都不能压入也不能输出则为错误。这是一种贪心算法思路,每一步求最优解。但这样没有考虑到有时候能压入栈1的情况下也必须压入栈2的情况。因此,其实贪心算法缺乏对于数据整体的先处理,或者是进行每一步时都对新数据进行存储和完善以便之后选择。2. 之后参考其他博客的思路,自己重新写,花了很多时间而且代码质量差。开始还是WA,后来发现模拟过程有误,又花了很多时间进行对特殊情况的修改。之后结果能够正确,但是因为自己加入边并存储的方式和dfs算法写的非常烂,结果超时。之后还是按照其他博客的参考代码进行修改。
3. 对于输出的模拟情况,需要考虑的内容很多,花了大量的时间进行修改,参考其他博客的题解,但是没有找到一个非常简洁而又正确的写法。总体而言,在图算法、离散数学相关的算法方面非常缺乏练习也没有付出努力去练习过。
- 参考资料
(解题思路)http://zhidao.baidu.com/link?url=olvz7jCjua-wBM8OBknsWkKOPYBVm5xOyY6evemWdDxMileb3U_UqNZebPuDPD_8kz5rmlU74BpmKrwnkrhKn_
(解题思路与代码)http://tieba.baidu.com/p/503212184,http://www.cnblogs.com/Blacko/p/3373313.html
(推荐参考代码)http://blog.csdn.net/kqzxcmh/article/details/9566813
- sicily双栈排序
- sicily--1930. 排序
- sicily 1930.排序
- Sicily 1142. 排序
- Sicily. 全排序输出
- Sicily 1930. 排序
- sicily-1930.排序
- Sicily 拓扑排序
- Sicily 1154 Easy Sort(排序)
- Sicily 1424 奖金(拓扑排序)
- Sicily.1046. Plane Spotting(排序)
- [sicily][排序]1154. Easy sort
- [sicily][排序]1134. 积木分发
- sicily Ordering Tasks 拓扑排序
- sicily 统计数字 map各种排序
- Sicily 1142. Pancake Sorting(煎饼排序)
- sicily 选择排序比较次数 期末测试
- Sicily 1142 排序 (SOJ 1142) 【搜索剪枝】
- 黑马培训之IOS学习--Foundation--dictionary字典的使用
- UICollectionView 与 storyboard 的一点体会
- CI框架源码阅读笔记1 - 环境准备、基本术语和框架流程
- asio学习笔记4
- vector,對string排序
- sicily双栈排序
- win7桌面c盘指向其他盘,或者从其它盘重新指向C盘...
- JS中的RegExp对象
- Validate Binary Search Tree
- Unicode编码转到utf8编码
- java NIO工作机制
- sdfsdf
- Ubuntu 12.04下Deepnet配置
- Android Audio System线性音量和对数音量的转换