这里有$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;
}
错误思路的代码

相关文章: