原题链接 https://www.luogu.com.cn/problem/P1754
题目大意
一个长度为 2n 的括号序列由 n 个左括号和 n 个右括号组成,问有多少种合法方案;
题解
显然对于任意时刻,要是能找开 B,那么之前一定有一个 A 给他提供了一张 50 的 Money,如果将 A 看做是一个左括号,将 B 看做是一个右括号,那么这道题就转化成了:求合法括号序列的方案数
相信对于这种括号序列匹配的问题,大佬一眼就能看出答案就是卡特兰数,但是我不会qwq 。
由于 xcg 正在练习 dp,所以我们还是用 dp 的方法来优雅的解决这道题吧qwq 。
状态设置
dp [ i ][ j ][ k ]:当前我们已经有 i 个球迷在排队了,其中有 j 个是 A,有 k 个是 B;
当然也可以把第一维优化掉,但是空间大真的是可以为所欲为的~
状态转移
对于一个长度为 i 的队列,一定是从一个长度为 i-1 的队列插入一个 A 和 B 得到的;
那么我们分别加上这两种情况的方案数即可;
注意时刻保证 A 的数量一定大于 B 的数量;
dp [ i ][ j ][ k ] += dp [ i-1 ][ j-1 ][ k ] + dp [ i-1 ][ j ][ k-1 ]
红色部分表示是插入了一个 A 后得到的
蓝色部分表示是插入了一个 B 后得到的
初始化
刚开始一个人也没有,方案数是 1,即:
dp [ 0 ][ 0 ][ 0 ] = 1
答案输出
n 个 A 和 n 个 B 都排好了,共有 2n 个人;
ans = dp [ 2n ][ n ][ n ]
Code:
#include<iostream> #include<cstdio> using namespace std; int read() { char ch=getchar(); int a=0,x=1; while(ch<'0'||ch>'9') { if(ch=='-') x=-x; ch=getchar(); } while(ch>='0'&&ch<='9') { a=(a<<1)+(a<<3)+(ch-'0'); ch=getchar(); } return a*x; } const int N=50; int n; long long dp[N<<2][N][N]; //dp[i][j][k]:有i个人在排队,其中有j个A类的,有k个B类的方案数 //注意到每时每刻A类的都不少于B类的 int main() { n=read(); dp[0][0][0]=1; //一个人也没有的方案数是1 for(int i=1;i<=2*n;i++) //已经有i个人在排队了,注意要枚举到2n { for(int j=i;j>=(i+1)/2;j--) //其中有j个A类的,保证A的人数一定大于B的人数 { int k=i-j; //有k个B类的 dp[i][j][k]+=dp[i-1][j-1][k]+dp[i-1][j][k-1]; //要么加进来一个A,要么加进来一个B } } printf("%lld ",dp[2*n][n][n]); //输出答案 return 0; }