合法括号序列(来源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)
