从一道递归题到卡特兰数,彻底掌握蓝桥杯中的“合法序列”类问题

合法括号序列(来源2020年蓝桥杯省模拟赛)

题目描述

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

由 1 对括号,可以组成一种合法括号序列:()

由 2 对括号,可以组成两种合法括号序列:()()(())

由 4 对括号组成的合法括号序列一共有多少种?

题解

  • 状态表示(l, r) 表示已用左/右括号数
  • 约束条件l >= r(任意前缀合法)
  • 目标状态(n, n)
  • 搜索方式:DFS + 剪枝(回溯)

这是所有"合法序列"问题的模板

cpp 复制代码
#include <iostream>
using namespace std;

int sum=0;
int n=4;
// 计算 n=4 时合法括号序列数量
void f(int l,int r)
{
  if(l<r)  //右括号不能多于左括号
    return;
  if(l>n||r>n)  //超过n对
    return;
  if(l==n&&r==n)
    sum++;

  f(l+1,r);   //加"("
  f(l,r+1);   //加")"
}

int main()
{
  f(0,0);
  printf("%d",sum);
  return 0;
}

问题变种1:输出所有合法括号序列

cpp 复制代码
void dfs(int l,int r,int n,char path[],int idx){
    if(l<r||l>n||r>n)
        return;
    if(l==n&&r==n){
        path[idx]='\0';
        puts(path);
    }
    path[idx]='(';
    dfs(l+1,r,n,path,idx+1);
    path[idx]=')';
    dfs(l,r+1,n,path,idx+1);
}

问题变种2:给定字符串,判断是否合法

只需一个计数器bal,当bal<0时说明右括号比左括号多,非法

cpp 复制代码
int isValid(char* s) {
    for (int i = 0; s[i]!='\0'; i++) {
        if (s[i] == '('
            bal++;
        else
            bal--;
        if (bal < 0)
            return 0;
    }
    return 1;
}

卡特兰数

引入

第一题改变n的值,得到一下结果

n=0 → 1 n=1 → 1 n=2 → 2 n=3 → 5 n=4 → 14 n=5 → 42

有经验的人会立刻想到这不就是卡特兰数嘛?

它是组合数学中一个非常经典、优美且应用广泛的数列

公式

快速计算卡特兰数

cpp 复制代码
// 适用于 n <= 19(long long 范围内)
long long catalan(int n) {
    if (n <= 1) return 1;
    long long c = 1;
    for (int i = 0; i < n; i++) {
        c = c * 2 * (2 * i + 1) / (i + 2); // 先乘后除,保证整除
    }
    return c;
}

卡特兰数的五大蓝桥杯应用场景

记住:只要问题满足"不能越过对角线"或"前缀约束",就可能是卡特兰数!


场景 1:出栈序列计数(栈混洗)

题目:1~n 按顺序入栈,问有多少种不同的出栈序列?

解法:
  • 直接返回 Catalan(n)
  • 若需输出所有序列 → 用 DFS 模拟入栈/出栈:
cpp 复制代码
void dfs(int push, int pop, int n, int stack[], int top, int out[], int out_idx) {
    if (out_idx == n) { /* 输出 out */ return; }
    if (push <= n) {
        stack[top] = push;
        dfs(push+1, pop, n, stack, top+1, out, out_idx); // 入栈
    }
    if (top > 0) {
        out[out_idx] = stack[top-1];
        dfs(push, pop+1, n, stack, top-1, out, out_idx+1); // 出栈
    }
}

场景 2:二叉搜索树(BST)结构数

题目:n 个不同节点,能构成多少种不同结构的 BST?

解法:
  • 答案 = Catalan(n)
  • 递推式:dp[i] = Σ dp[j] * dp[i-1-j] (j 为左子树节点数)
cpp 复制代码
int numTrees(int n) {
    int dp[n+1];
    dp[0] = dp[1] = 1;
    for (int i = 2; i <= n; i++) {
        dp[i] = 0;
        for (int j = 0; j < i; j++)
            dp[i] += dp[j] * dp[i-1-j];
    }
    return dp[n];
}

📌 蓝桥高频 DP 模板!


场景 3:不穿越对角线的网格路径

题目:n×n 网格,从 (0,0) 到 (n,n),只向右/上走,且不能走到 y > x 区域,求路径数。

解法:
  • 答案 = Catalan(n)
  • 若需输出路径 → 用 (x,y) 代替 (l,r),约束 y <= x
cpp 复制代码
void path(int x, int y, int n) {
    if (y > x || x > n || y > n) return;
    if (x == n && y == n) { /* 记录路径 */ return; }
    path(x+1, y, n); // 向右
    path(x, y+1, n); // 向上
}

场景 4:多边形三角剖分(较少见,但可能出)

题目:凸 (n+2) 边形,用不相交对角线分成三角形,有多少种分法?

解法:
  • 答案 = Catalan(n)
  • 通常只需输出数值,用 DP 或公式即可

场景 5:买票问题(经典变形)

题目:2n 个人排队买票,票价50元。n 人持50元,n 人持100元。初始售票处无零钱,问有多少种排队方式不会导致找不开钱?

分析:
  • 持50元 = '(',持100元 = ')'
  • 任意前缀中 50元人数 ≥ 100元人数 → 合法括号序列
  • 答案 = Catalan(n)
相关推荐
只是懒得想了5 小时前
C++实现密码破解工具:从MD5暴力破解到现代哈希安全实践
c++·算法·安全·哈希算法
m0_736919105 小时前
模板编译期图算法
开发语言·c++·算法
dyyx1115 小时前
基于C++的操作系统开发
开发语言·c++·算法
m0_736919105 小时前
C++安全编程指南
开发语言·c++·算法
蜡笔小马5 小时前
11.空间索引的艺术:Boost.Geometry R树实战解析
算法·r-tree
-Try hard-5 小时前
数据结构:链表常见的操作方法!!
数据结构·算法·链表·vim
2301_790300966 小时前
C++符号混淆技术
开发语言·c++·算法
我是咸鱼不闲呀6 小时前
力扣Hot100系列16(Java)——[堆]总结()
java·算法·leetcode
嵌入小生0076 小时前
单向链表的常用操作方法---嵌入式入门---Linux
linux·开发语言·数据结构·算法·链表·嵌入式
LabVIEW开发6 小时前
LabVIEW金属圆盘压缩特性仿真
算法·labview·labview知识·labview功能·labview程序