P1754 球迷购票问题

原题链接  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\n",dp[2*n][n][n]);  //输出答案 
    return 0;
}

 

 

 

 

posted @ 2019-12-12 17:10  暗い之殇  阅读(178)  评论(0编辑  收藏  举报