题意:
若一个栈的输入序列为1、2、...、n
,求其可能的出栈顺序,并输出前20
种方案。
思路:
我们可以将搜索中的每一个状态用三者来表示:state1
(已经出栈的答案序列,用vector
存),state2
(还在栈中的序列,用一个stack
存),state3
(还未进栈的序列,直接用一个指向 即将入栈元素 的指针变量表示,因为未进栈的车是有序的)。
我们实际操作只有两种:
-
op1:车栈
state2
弹出栈顶元素并加入state1
答案序列 -
op2:将
state3
所指向的元素压入state2
车栈
由于考虑到字典序的原因我们可以 先递归枚举op1 再 递归枚举op2,因为op1
表示 出栈,出栈的数必定比进栈的数小(维持字典序的核心)。
直到把所有n
个数全部弹出,此为边界情况,输出一种方案。
此外,由于题目要求输出前20
种方案,我们设置一个全局变量cnt=20
,每输出一个方案就减1
,当减到0
时就return
。
时间复杂度:
O(2^N)
(每一步可以有两种操作,n
个元素复杂度就是2^n
,n
的数据范围较小,n<=20
,大约是1e6
)
代码:
#include<bits/stdc++.h>
using namespace std;
int n;
//三者共同维护深搜过程中的每个状态
vector<int> state1; //答案序列
stack<int> state2; //车栈
int state3 = 1; //相当于一个指向 即将入栈元素 的指针,初始为1,后续不断加一
int cnt = 20;
void dfs()
{
if(!cnt){
return ; //如果输出的方案满20个就直接return
}else if(state1.size()==n){ //当答案序列大小满n个元素时 输出一组方案
for(int i=0;i<n;++i) cout<<state1[i];
cout<<endl;
--cnt;
return ;
} //分支一:车栈state2弹出栈顶元素并加入state1答案序列
if(state2.size()){ //车栈不空我们才进行弹出操作
state1.push_back(state2.top()), state2.pop();
dfs();
state2.push(state1.back()), state1.pop_back(); //恢复现场方便回溯查找其他方案
} //分支二:将state3所指向的元素压入state2车栈
if(state3<=n){ //保证state3指针不出界
state2.push(state3), ++state3;
dfs();
state2.pop(), --state3;
}
}
int main()
{
cin>>n;
dfs();
return 0;
}