Catalan数

【问题描述】对于一个栈,已知元素的进栈序列,判断一个由栈中所有元素组成的排列的出栈序列。

有N个数,则代表入栈序列为1,2,3,4,5...,N。求所有可能的出栈序列和总数。

代码如下

#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
#include<stdio.h>
using namespace std;
int N = -1;
int sum = 0;;
//递归算法
//count:当前已经入栈的元素个数
//seq:当前栈
//result:出栈序列,从左到右为出栈序列
void printValidSequence(int count,vector<int> seq,vector<int> result){
    if (count == N){
        result.insert(result.end(), seq.rbegin(), seq.rend());
        for (int i = 0; i < result.size(); ++i){
            cout << result[i];
        }
        cout << endl;
        sum++;
        return;
    }
    vector<int> tmp(seq.begin(), seq.end());
    seq.push_back(++count);    //下一个元素入栈
    printValidSequence(count, seq, result);
    if (!tmp.empty()){         //当前元素出栈出栈
        result.push_back(tmp.back());
        tmp.pop_back();
        printValidSequence(count-1, tmp, result);
    }    
}
//非递归算法
//递归算法
//count:当前已经入栈的元素个数
//seq:当前栈
//result:出栈序列,从左到右为出栈序列
class node{
public:
    vector<int> seq;
    vector<int> result;
    int count;
    node(){
        seq.clear();
        result.clear();
        count = 0;
    }
    void print(){
        result.insert(result.end(), seq.rbegin(), seq.rend());
        for (int i = 0; i < result.size(); ++i){
            cout << result[i];
        }
        cout << endl;
    }
    void add(){
        result.push_back(seq.back());
        seq.pop_back();
    }
    void push(){
        seq.push_back(++count);
    }
};
void printValidSequence(int N){
    vector<node> STACK;
    STACK.push_back(node());
    while (true){
        if (STACK.empty()){
            break;
        }
        node tmp = STACK.back(); //出栈
        node tmp2 = tmp;//入栈(=是拷贝)
        STACK.pop_back();
        if (tmp.count == N){
            tmp.print();
            sum++;
        }
        else{
            if (!tmp.seq.empty()){
                tmp.add();
                STACK.push_back(tmp);
            }
            tmp2.push();
            STACK.push_back(tmp2);
        }
    }
}
int main(){
    cin >> N;
    vector<int> seq;
    vector<int> result;
    printValidSequence(0, seq, result); //递归算法 
    cout << "总数为" << sum << endl;
    sum = 0;
    printValidSequence(N);  //非递归算法
    cout << "总数为" << sum << endl;
    system("pause");
    return 0;
}

也可以用Catalan数求解。

h(n)=h(0)h(n-1)+h(1)h(n-2)+...+h(n-1)h(0)  h(0)=h(1)=1  <=> h(n)=C(2n,n)*(1/(1+n))  (C是组合符号)

对于本道题:

    首先,我们设h(n)=序列个数为n的出栈序列种数。我们假定,最后出栈的元素为k,显然,k取不同值时的情况是相互独立的,也就是求出每种k最后出栈的情况数后可用加法原则,由于k最后出栈,因此,在k入栈之前,比k小的值均出栈,此处情况有f(k-1)种,而之后比k大的值入栈,且都在k之前出栈,因此有f(n-k)种方式,由于比k小和比k大的值入栈出栈情况是相互独立的,此处可用乘法原则,f(n-k)*f(k-1)种,求和便是Catalan递归式。

    因此种类数是h(n)=C(2n,n)*(1/(1+n))

本题和另外一道题也是等价的:

     一位大城市的律师在她住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班。如果他
从不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?
我们使用深度优先搜索来完成这道题目;
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
#include<stdio.h>
using namespace std;
int N = -1;
int sum = 0;
class node{
public:
    int x;
    int y;
    int dir;  //1,2分别代表右和上
    node(){
        x = 0;
        y = 0;
        dir = 0;
    }
    node(int _x,int _y,int _dir){
        x = _x;
        y = _y;
        dir = _dir;
    }
    int sum(){
        return x + y;
    }
    bool compare(){
        if (x > y){
            return true;
        }
        else{
            return false;
        }
    }
};
void searchDest(int N){
    vector<node> STACK;
    STACK.push_back(node());
    while (true){
        if (STACK.empty()){
            break;
        }
        else{
            node topNode = STACK.back();
            if (topNode.sum() == 2 * N){ //到达终点
                sum++;
                vector<int> stack1;//模拟出入栈,得到输出序列,向右走为入栈,向上走为出栈
                vector<int> result;
                for (int i = 1; i < STACK.size(); ++i){                    
                    if (STACK[i].y - STACK[i - 1].y>0){                        
                        result.push_back(stack1.back());
                        stack1.pop_back();
                    }
                    else{
                        stack1.push_back(STACK[i].x);
                    }
                }
                for (int i = 0; i < result.size(); ++i){
                    cout << result[i];
                }
                cout << endl;
                STACK.pop_back();
            }
            else{
                if (topNode.dir == 0){
                    STACK.back().dir++;
                    if (topNode.x < N){
                        STACK.push_back(node(topNode.x + 1, topNode.y, 0));;//向右走
                    }
                }
                else if (topNode.dir == 1){
                    STACK.back().dir++;
                    if (topNode.compare()){
                        STACK.push_back(node(topNode.x, topNode.y + 1, 0)); //向上走
                    }
                }
                else if (topNode.dir == 2){
                    STACK.pop_back();
                }
            }
        }
    }
}
int main(){
    cin >> N;
    searchDest(N);
    cout << "总数为:" << sum << endl;
    system("pause");
    return 0;
}

还有一个问题是,给出一个序列判断该序列是否为合法的输出序列:

比如输入序列是:1,2,3,4,5

序列53421就是合法序列:

有如下的判定定理:
对于出栈序列中的每一个数字,在它后面的、比它小的所有数字,一定是按递减顺序排列的。<=> 该输出序列是合法的

 

 

posted on 2017-04-02 21:20  codeDog123  阅读(226)  评论(0编辑  收藏  举报

导航