好久没写递归了,怕手生再来练练手。
题意:车轨上有上图所示的n个中转栈,现有2n个列车,给出列车初始编号序列。列车从最右边驶入车轨,并且列车只能从右向左移动,要求给出列车中转操作序列,使列车经过这n个中转栈后从最左端输出时满足1~2n的排列。
分析:其实这不就是火车出栈问题吗,可以用分治递归的思路,首先使一半有序,然后再合并使整体有序。
思路:具体来讲就是,一段无序的序列(编号1~2n),通过一个中转栈使1 ~ 2n-1在整个序列的前面2n-1+1 ~ 2n在整个序列的后面;再经过下一中转栈时,又能进一步进行排序,这样在最后一个中转栈是就只是两两之间的有序无序问题了。
什么都不如例子来的好使,比如n=3时,初始序列为 2 1 5 8 7 3 6 4
经过栈1时,使序列满足如下形式:2 1 3 4 | 6 7 8 5
经过栈2时,使序列满足如下形式:2 1 | 3 4 | 6 5 | 8 7
经过栈3时,使序列满足如下形式:1 2 | 3 4 | 5 6 | 7 8
然后就达到要求了。
那么该如何操作才能达到上述状态呢?
比如经过栈1时,可将序列分成包含(1~4)和(5~8)的两段子序列。那么每来一个数,判断该数是否大于4,若小于等于4,则该列车进栈接着出栈;否则该列车暂时停放在栈中,等所有小于等于4的列车出栈后再按照先进后出的性质出栈,这样就达到了目的;
经过栈1时的操作序列:2 2 1 1 5 8 7 3 3 6 4 4 6 7 8 5
经过下面的栈的情况就类似了,不再赘述了。
附代码:
1 #include <iostream> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cstdio> 5 #include <stack> 6 using namespace std; 7 const int maxn = 5000; 8 int a[maxn]; 9 void dfs(int num[], int r, int k) 10 { 11 if(k <= 1) return ; 12 int s1[k/2 + 1]; 13 int s2[k/2 + 1]; 14 int k1 = 0, k2 = k/2; 15 int l = r-k; 16 int mid = (l + r)/2; 17 18 stack<int> si; 19 int p1, p2; p1 = p2 = 0; 20 for(int i = 0; i < k; i++) 21 { 22 if(num[i] <= mid) 23 { 24 s1[p1++] = num[i]; 25 printf("%d %d ", num[i], num[i]); 26 } 27 else 28 { 29 printf("%d ", num[i]); 30 si.push(num[i]); 31 } 32 } 33 while(!si.empty()) 34 { 35 s2[p2] = si.top(); 36 printf("%d ", s2[p2++]); 37 si.pop(); 38 } 39 dfs(s1, mid, k/2); 40 dfs(s2, r, k/2); 41 return ; 42 } 43 44 45 int main() 46 { 47 int n; 48 while(~scanf("%d", &n) && n) 49 { 50 int m = 1<<n; 51 for(int i = 0; i < m; i++) 52 scanf("%d", &a[i]); 53 dfs(a, m, m); 54 printf("\n"); 55 } 56 return 0; 57 }