这里有$n$列火车将要进站再出站,但是,每列火车只有$1$节,那就是车头。
这$n$列火车按$1$到$n$的顺序从东方左转进站,这个车站是南北方向的,它虽然无限长,只可惜是一个死胡同,而且站台只有一条股道,火车只能倒着从西方出去,而且每列火车必须进站,先进后出。
也就是说这个火车站其实就相当于一个栈,每次可以让右侧头火车进栈,或者让栈顶火车出站。
车站示意如图:
出站<—— <——进站
|车|
|站|
|__|
现在请你按《字典序》输出前$20$种可能的出栈方案。
输入格式
输入一个整数$n$,代表火车数量。
输出格式
按照《字典序》输出前$20$种答案,每行一种,不要空格。
数据范围
$1 \leq n \leq 20$
输入样例:
3
输出样例:
123 132 213 231 321
解题思路
我的思路
首先,看到数据范围大小就知道应该要用dfs了。
这道题虽然打上了简单的标签,但当时想了很久,最后还是用直觉来AC的。
说一下我当时的思路吧,一开始想错了,但最后的思路与正解几乎一样。
我一开始是怎么想的呢。因为搜索嘛,所以肯定先想搜索的顺序或状态。所以我一开始想的是,分两种状态,一种是一个数字压栈里,另一种是数字出栈,所以搜索的顺序是先把数字压栈里,然后继续从下一个数字开始搜。当回溯到这个数字时,再把数字从栈顶弹出,然后继续搜。同时因为我是按数字从小到大的方式搜的,所以保证数字的的出入栈顺序是合法的(因为只有小的数字入栈后,后面的数字才可以入栈)。
按照这个思路,然后我就写出了这样的代码。
#include <cstdio> #include <vector> #include <queue> #include <algorithm> using namespace std; const int N = 30; int q[N], hh, tt = -1; int stk[N], tp; priority_queue<vector<int>, vector<vector<int>>, greater<vector<int>>> pq; void dfs(int cnt, int n) { // 递归边界为搜索到最后一个数字 if (cnt == n) { vector<int> ret; // 由于出栈的元素压到队列,所以先把队列的元素压入数组中 for (int i = hh; i <= tt; i++) { ret.push_back(q[i]); } // 同时由于最后一个数字要先压入栈,然后又最先从栈弹出,所以干脆直接把最后一个数字压入数组中 ret.push_back(n); // 最后把栈中的元素压入数组中 for (int i = tp; i; i--) { ret.push_back(stk[i]); } // 把出栈的结果压入优先队列,到时候直接从优先队列中输出前20个结果就可以了 pq.push(ret); return; } stk[++tp] = cnt; // 先把数字压入栈,然后往下一个数字搜 dfs(cnt + 1, n); q[++tt] = stk[tp--];// 恢复现场,同时把数字从栈顶压入队列 dfs(cnt + 1, n); // 继续从下一个数字搜 tt--; } int main() { int n; scanf("%d", &n); dfs(1, n); for (int i = 0; !pq.empty() && i < 20; i++) { vector<int> tmp = pq.top(); pq.pop(); for (auto &it : tmp) { printf("%d", it); } printf("\n"); } return 0; }