• luogu P1044 火车进出栈问题(Catalan数)


    Catalan数就是魔法

    火车进出栈问题即:

    一个栈(无穷大)的进栈序列为 1,2,3,4,...,n 求有多少个不同的出栈序列?

    将问题进行抽象, 假设'+'代表进栈, 则有'-'代表出栈

    那么如果进栈序列为123, 则:

    + + + - - - 将1, 2, 3压入栈后再将3, 2, 1弹出 得到出栈序列为321

    同样, + - + - + - 得到出栈序列为123

    上面所述的均为合法进出栈的序列 可发现规律: 序列中 + 的个数等于 - 的个数

    但是如 + - - + +  - 这样的序列, 在栈为空时仍进行弹出操作的, 为非法序列

    进一步将 + - 序列抽象到平面直角坐标系中, + 代表向右走一格, -代表向上走一格, 可知y = x直线下方的序列均为合法序列, 如下图所示:

    到这里, 火车进出栈问题就已经抽象为非降路径(只能向上走或向右走的路径)的问题

    对于路径计数问题:

    1)从(0,0)到(n,n)的非降路径等于n个x和n个y的排列数 即路径数为C(2n, n);

    2)如果不是合法序列, 则会经过上图中的红线, 不合法序列一定会经过点(n -1, n), 从(0, 0)到(n -1, n)有C(2n, 2n-1)种不合法的路径;

    3)综合1), 2) 并依据组合数性质化简

    推导出公式:

     

    我们可以得到出栈序列的总数为C(2n, n) / (n + 1).

    下面以luogu P1044 栈为例, 完整代码如下:

    /*
     * @Author: Hellcat
     * @Date: 2020-02-27 15:54:37
     */
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    ll C(int n, int m) {
        ll ans = 1;
        for(ll i = 1; i <= m; i++)
            ans = ans * (n - m + i) / i;
        return ans;
    }
    
    int main() {
        int n;
        scanf("%d", &n);
        printf("%lld\n", C(2*n, n) / (n + 1));
    }

    题给数据量不大 选择高效的组合数计算方法即可 无需高精度算法.

    同时给出完全模拟的dfs暴力搜索算法如下:

    dfs时候需要注意 为保证字典序输出优先考虑出栈.

    #include <bits/stdc++.h>
    using namespace std;
    
    int n, cnt = 20;
    vector<int> state1;
    stack<int> state2;
    int state3 = 1;
    
    int res = 0;
    
    void dfs() {
        if(!cnt) return;
        if(state1.size() == n) {
            res++;
            cnt--;
            for(auto x : state1) cout<<x;
            puts("");
            return;
        }
        // 按字典序输出 则出栈先
        if(state2.size()) { // 出栈
            state1.push_back(state2.top());
            state2.pop();
            dfs();
            state2.push(state1.back()); // 回溯
            state1.pop_back();
        }
        if(state3 <= n) { // 入栈
            state2.push(state3);
            state3++;
            dfs();
            state3--; // 回溯
            state2.pop();
        }
    }
    
    int main() {
        scanf("%d", &n);
        dfs();
        printf("%d\n", res);
    }

    输入

    3

    输出

    123
    132
    213
    231
    321
    5

  • 相关阅读:
    NOI-01:查找最接近的元素 基本二分
    C#学习笔记之——数据库操作的相关类
    Lua学习笔记——环境安装(Windows和MacOS)和在MacOS安装时错误解决方法
    Ubuntu下对数据库的操作
    Git常用操作
    [Unity游戏开发]常用类之Time类
    [Unity游戏开发]四元数Quaternion
    [Unity游戏开发]常用类之Transform类
    [Unity游戏开发]常用类之Component类
    [Unity游戏开发]射线(Ray)
  • 原文地址:https://www.cnblogs.com/tedukuri/p/12373422.html
Copyright © 2020-2023  润新知