排队与卡特兰数

数据结构课上讲栈,老师给了这样一个题目。

12个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?  

实际上只要上过和栈有关的课程的人应该都猜得出来这是与卡特兰数有关。但是,怎么找到这个问题和卡特兰数之间的关系呢?这就要让我们去思考卡特兰数到底有什么样的应用场景。我们说到卡特兰数,第一个想到的肯定就是出栈序列的数目。但是这里似乎不太容易通过“栈”这个方向找到相关性。

想了很久之后我想到了一点,卡特兰数与上三角路径规划问题的关系。

上三角路径规划:律师在住所以北n个街区和以东n个街区处工作,每天走2n个街区去上班。如果她不穿越(但可以碰到)从家到办公室的对角线,有多少条可能道路? 

 我为什么会想到这两者之间的关系呢?上三角路径规划中“不穿越对角线”这一点要求“y一直大于等于x”,这不是与“而且第二排比对应的第一排的人高”有点像吗?可是这样关系还是不清晰。我们怎么把上三角路径规划的一个解与本题中的一个解对应起来呢?

注意到,上三角路径规划中,律师行走的路线可以简单的划分为两类,向上走和向右走。这就像我们本题中把人划分为前后两排一样。律师先走过坐标值较小的线段,后走过坐标值较大的线段。这就像我们本题中不管哪一排都要从矮到高排列。

所以最后我领悟到,我们可以把12个人先从矮到高排序,对于一个上三角路径规划的解,我们这样操作:律师第i步如果向上走,我们就把第i个人拉到第一排,向右走我们就把第i个人拉到第二排。这就可以得到本题的一个解。

然后也很容易想清楚两个问题的解是一一对应的。

所以本题答案是6的卡特兰数。

下面写了代码验证一下。

#include <iostream>
#include "stack.h"
using namespace std;

stack<int> memory(12);//0~5表示第一排,6~11表示第二排
int ans = 0;

bool peace()
{
    for (int i = 0; (i < 5) && (i < memory.get_size() - 1); ++ i){
        if (*(memory[i]) >= *(memory[i + 1])) {
            return false;
        }
    }
    for (int i = 6; (i < 11) && (i < memory.get_size() - 1); ++ i){
        if ((*(memory[i]) >= *(memory[i + 1])) || (*(memory[i]) <= *(memory[i - 6]))) {
            return false;
        }
    }
    if ((memory.get_size() == 12) && (*(memory[11]) <= *(memory[5]))){
        return false;
    }
    for (int i = 1;i < memory.get_size(); ++ i) {
        for (int j = 0; j < i; ++ j) {
            if (*(memory[i]) == *(memory[j])) {
                return false;
            }
        }
    }
    return true;
}

void dfs(int layer)
{
    if (layer == 12) {
        ans ++;
        for (int i = 0; i < 12; ++ i) {
            cout << *(memory[i]) << " ";
        }
        cout << endl;
    } else {
        for (int i = 0; i < 12; i ++) {
            memory.push(i);
            if (peace()) {
                dfs(layer + 1);
            }
            memory.pop();
        }
    }
}

int main()
{
    dfs(0);
    cout << ans << endl;
    return 0;
}

结果就是132。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值