第十二届蓝桥杯B组省赛括号序列题解

请添加图片描述

问题分析

首先要把括号序列的合法性表示出来

  • 定义变量C初始值为0
  • 从左向右遍历序列,发现左括号C=C+1,发现右括号C=C-1
  • 最终C==0且C不曾为负值则序列合法

类似的,我们可以定义左括号合法性

  • 定义变量C初始值为0
  • 从左向右遍历序列,发现左括号C=C+1,发现右括号且C>0时C=C-1,否则C不变化
  • 如果最终C==0则左括号合法

右括号合法性

  • 定义变量C初始值为0
  • 从右向左遍历序列,发现右括号C=C+1,发现左括号且C>0时C=C-1,否则C不变化
  • 如果最终C==0则右括号合法

定义AL、AR、ANS

  • AL:添加最少的括号使得左括号合法的方案数
  • AR:添加最少的括号使得右括号合法的方案数
  • ANS:添加最少的括号使得括号序列合法的方案数
    假设序列中原本有某个右括号无法使C的值发生变化(就是找不到匹配的左括号),那么在序列中添加右括号使得某个左括号被匹配后,那个单着的右括号仍然不可能与任何左括号匹配。当然,原本就匹配成功的右括号也不会失去左括号。这就意味着左括号合法性与右括号合法性是相互独立的。所以ANS=AL*AR

计算方法

对于如下序列,写出检查左括号合法性时C的变化过程

((( ) (( )
(((->123
)->122
((->12234
)->12233

C的数值其实是当前的空隙中最多可能插入的右括号数
所以在遇到右括号时就将该空隙的最大容量减一而不是添加一个新状态。
使左括号合法的过程就是在容量分别为12233的五个空隙中插入三个右括号。但要注意,还有其它约束,比如把三个括号都放到前两个空里就不合法。
这个约束就是:

  • 容量为n的空隙前面不可以插入超过n个括号

我们把0到”C状态序列的长度“放在外层循环,当前在第几个空隙放到内层循环,F[n,c]表示将n个括号放到前c个空隙的方案数。
F的初始值如下(不管算到哪个空隙,插入0个括号的方案数一定是1)

  [0 1 2 3 4 5] ->第几个空隙
0  1 1 1 1 1 1
1  0 0 0 0 0 0
2  0 0 0 0 0 0
3  0 0 0 0 0 0
4  0 0 0 0 0 0
5  0 0 0 0 0 0
|
插入几个括号

对于((()
状态序列为122
F会成为

1 1 1 1
0 1 2 3
0 0 2 5

F[n,c]=F[n,c-1]+F[n-1.c-1]+F[n-2,c-1]…
当前项=当前空里插入0个括号+当前空里插入1个括号+当前空里插入2个…
=前c-1个空放n个括号+前c-1个空放n-1个括号…
约束条件是n<C[c]
最终我们要的结果在F[最后一个空隙的容量,空隙的总个数]

#include<bits/stdc++.h>
using namespace std;
#define NMAX 5000
#define VMA1 1000000007
int N,L;
int RES[NMAX+1];//C状态表(空隙状态表)
long long F[NMAX+1][NMAX+1];
string input ;
void init(){//检查合法性,生成空隙状态表
    L=0;
    for(int i=1;i<=N;++i){
        if(input[i-1]=='('){
            ++L;
            RES[L]=RES[L-1]+1;
        }else{
            if(RES[L]>0){
                RES[L]--;
            }
        }
    }
}
long long calc(){//计算使左括号合法的方案数
    memset(F,0,sizeof(F));
    //对所有的空隙一个括号也不插入的方案只有一种
    for(int c=0;c<=L;++c){
        F[0][c]=1;
    }
    //插入一个,两个...
    for(int n=1;n<=L;++n){
    	//第一个空、第二个空...
        for(int c=1;c<=L;++c){
        	//满足约束条件
            if(n<=RES[c]){
            	//当前放0个、1个...、放满或放n个
                /*for(int i=n;i>=0;--i){
                    F[n][c]+=F[i][c-1];
                    F[n][c]%=VMA1;//题目要求的防溢出
                }*/
                //F[n][c-1]就是当前放1个+当前放...
                F[n][c]=F[n-1][c]+F[n][c-1];
                F[n][c]%=VMA1;//题目要求的防溢出
            }
        }
    }
    return F[RES[L]][L];
}
int main(){
    cin >> input;
    N=input.size();
    init();//获取空隙状态表(左)
    long long v_left=calc();//计算使左括号合法方案数
    //括号种类反转
    for(int i=0;i<N;++i){
        input[i]=(input[i]=='('?')':'(');
    }
    //序列反转
    reverse(input.begin(),input.end());
    init();//获取空隙状态表(右)
    long long v_right=calc();//计算使右括号合法方案数
    cout << (v_left*v_right)%VMA1 << endl;//输出ANS
    return 0;
}

注:尽力写明白了,看不懂请参考 2021蓝桥杯省赛java组-J括号序列(满分题解).

  • 6
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值