Question:

Solve:

声明:全文为蓝桥杯官方题解的重新思考整理,众所周知我写这道题写出事故了,可能解释的也会很难懂~

不难想到是dp,但是怎么dp真的不好想

参数解释:

cntl, cntr  原括号序列想要合法所需填充的左、右括号数

pos1, pos2  原括号序列所含有的左(右)、右(左)括号数

dp[ i ][ j ]   

dp数组,数值表示第 i 个左(右)括号位置前一共填充 j 个右(左)括号的方案数

pre[ i ] 

前缀和维护数组,填充括号数小于等于 i 的总方案数

minn[ i ] 

最小填充数组,表示第 i 个左(右)括号位置前面至少要填充的右(左)括号数

解题历程:

step 1:原括号序列想要合法所需填充的左、右括号数计算

方法:

遍历s,遇到 " ) " 时 cnt 减一,反之加一,当cnt < 0 时,cnt清零,同时cntl加一,遍历结束之后,cnt 的值也就是所需要的左括号数目,下面举个例子:

step 2:主体,只填充某种括号的方案数计算

dp基本思考:

用左括号的填充为例~

(dp第一维)

每一次进行括号填充的位置一定是在每一个右括号所在的位置,所以,以每一个右括号的位置 pos 为切入点,思考填充方案:

(dp第二维)

首先,假定最近两个右括号的位置分别为pos_1, pos_2 (pos_2 > pos_1)

那么,对于前 pos_2 的子串

所填充的左括号数必须大于最小填充数 minn[pos_2],小于总填充的最大数目 cntl

(dp第三维)

在上述范围内随便取一个数 num ,我们就可以选择在前 pos_1 的子串里添加 num1 个左括号,然后在 pos_2 的位置填充(num - num1)个左括号,从而实现前 pos_2 的子串一共填充 num 个左括号的要求,不难知道num1的范围是[ 0 ~ num ],严格来说应该是[ minn[pos_1] ~ num ]

那么前 pos_2 的子串一共填充 num 个左括号的方案数也就是:对 num1 从 0 取到 num 时 pos_1位置的填充方案求和

状态转移方程:

上述已经分析出 pos_2 位置的方案数可以用 pos_1 位置的方案数来表示,现在建立 dp 主体:

第一维的核心在于右括号的位置,其实也是右括号的出现序数,所以用 dp[ i ] 来表示第 i 个右括号

第二维的核心在于填充的左括号数目范围

第三维的核心在于对每个填充数的具体讨论

所以dp[ i ][ j ] 的 j 来表示填充数,结合所求的是方案数,所以数组整体含义:第 i 个右括号位置前一共填充 j 个左括号的方案数

结合所有分析,现在就可以得到一个关系式了:

dp[ i ][ j ] = sum( dp[i-1][0] + ... + dp[i-1][j])

dp优化方向:

得到上述的关系式以后,我们思考程序:

1.明确这是一个三层的循环

第一层从 1 到 右括号的总数目,是对dp数组的 i 遍历

第二层从第 i 个右括号位置的最小填充数到最大填充数,是对dp数组的 j 遍历

第三层是从 0 到 j 的遍历,表示关系式里的求和赋值

只有经过这三层循环之后,才能得到最终的结果dp[ pos1 ][ cntl ]

可以注意到,在第三层循环里,会不断的计算前缀和这个东西,那我就可以再开一个 pre 数组去保存前缀和,从而把第三层循环去掉,变成直接赋值,保证程序能够AC

2.具体怎么实现二维优化

我们知道,在第二层里填充数有一个范围[ minn[pos], cntl ]

而变成赋值之后,也就是去掉第三层循环, 添加式子 dp[ i ][ j ] = pre[ j ] (pre[ j ]含义看文章开头)

所以可以得到这样的一个逻辑式

并且,赋值前后要先将这个逻辑式实现

step 3:反转计算

上面的过程只是分析了填充左括号的方案数,还有填充右括号,二者是独立的,所以需要再次执行上述的过程,然后将两次的方案数相乘

其中涉及到对于原序列括号数、最小填充数minn[]的重新计算,可以将原括号序列彻底翻转来进行计算,比如括号序列")()((()" ,翻转为"()))()("

整个的具体实现看代码吧~

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;const int mod = 1e9+7;
string s;
int cntl, cntr, cnt;
ll dp[5010][5010], pre[5010], minn[5010];
//函数参数(需要填充的对应括号数,是否为左括号填充)
ll solve(int ans, bool isl){if(ans == 0) return 1;//初始化memset(dp,0,sizeof(dp));memset(pre,0,sizeof(pre));memset(minn,0,sizeof(minn));//括号反转if(!isl){for(int i = 0; s[i]; i++){if(s[i] == '(') s[i] = ')';else s[i] = '(';}reverse(s.begin(), s.end());}//计算第pos个左(右)括号前最少需要的右(左)括号数minn[pos]int pos1 = 0, pos2 = 0;for(int i = 0; s[i]; i++){if(s[i] == ')')minn[++pos1] = pos2;elsepos2++;}//dp,pre初始化if(minn[1] > 0) pre[0] = dp[1][0] = 1;for(int i = 1; i <= ans; i++){dp[1][i] = 1; pre[i] = pre[i-1] + 1;}//第一维:从2到所含有的最大对应括号数for(int i = 2; i <= pos1; i++){//第二维//小于最小填充数部分前缀和更新为0for(int j = 0; j < i-minn[i]; j++) pre[j] = 0;for(int j = i-minn[i]; j <= ans; j++){dp[i][j] = pre[j];//在填充范围内部分更新前缀和if(j - 1 < 0) pre[j] = dp[i][j];else pre[j] = (pre[j-1] + dp[i][j]) % mod;}}return dp[pos1][ans];
}
int main(void) {cin >> s;//计算所需括号数量cnt = cntl = cntr = 0;for (int i = 0; s[i]; i++) {if (s[i] == '(') cnt++;else cnt--;if (cnt < 0) {cntl++;cnt = 0;}}cntr = cnt;//调用函数输出结果cout << solve(cntl, true) * solve(cntr, false) % mod;return 0;
}

最后附上蓝桥杯汇总链接:蓝桥杯C/C++A组省赛历年真题题解

声明:图片均来源于蓝桥杯官网,以个人刷题整理为目的,如若侵权,请联系删除~

括号序列(DP优化)相关推荐

  1. Codeforces Beta Round #5 C. Longest Regular Bracket Sequence 括号序列 dp+栈

    点击打开链接 题意: 给你一个括号序列,让你找到最长的连续的合法括号序列 然后让你输出这个括号序列的长度是多少 这么长的括号序列一共有多少个 思路: 看到括号匹配,就用stack来弄就好了 然后我们d ...

  2. UVA 1626括号序列DP

    题意: 题目背景: 我们将正规括号序列定义如下: 空序列是正规括号序列. 如果S是一个正规括号序列,那么(S)和[S]都是正规括号序列. 如果A和B都是正规括号序列,那么AB是一个正规括号序列. 例如 ...

  3. [LOJ#2878]. 「JOISC 2014 Day2」邮戳拉力赛[括号序列dp]

    题意 题目链接 分析 如果走到了下行车站就一定会在前面的某个车站走回上行车站,可以看成是一对括号. 我们要求的就是 类似 代价最小的括号序列匹配问题,定义 f(i,j) 表示到 i 有 j 个左括号没 ...

  4. UVA1626 括号序列 Brackets sequence(区间DP匹配括号,输出匹配方案)

    整理的算法模板合集: ACM模板 UVA1626 Brackets sequence 我们将正规括号序列定义如下: 空序列是正规括号序列. 如果 SSS 是一个正规括号序列,那么 (S) 和 [S] ...

  5. [BZOJ 4350]括号序列再战猪猪侠 题解(区间DP)

    [BZOJ 4350]括号序列再战猪猪侠 Description 括号序列与猪猪侠又大战了起来. 众所周知,括号序列是一个只有(和)组成的序列,我们称一个括号 序列S合法,当且仅当: 1.( )是一个 ...

  6. BZOJ4350: 括号序列再战猪猪侠(区间DP)

    传送门 括号序列与猪猪侠又大战了起来. 众所周知,括号序列是一个只有(和)组成的序列,我们称一个括号 序列S合法,当且仅当: 1.( )是一个合法的括号序列. 2.若A是合法的括号序列,则(A)是合法 ...

  7. BZOJ4350: 括号序列再战猪猪侠【区间DP】

    Description 括号序列与猪猪侠又大战了起来. 众所周知,括号序列是一个只有(和)组成的序列,我们称一个括号序列S合法,当且仅当: 1.( )是一个合法的括号序列. 2.若A是合法的括号序列, ...

  8. 138.括号序列(区间型DP)

    3657 括号序列 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 我们用以下规则定义一个合法的括号序列: (1 ...

  9. Vijos P1448 校门外的树【多解,线段树,树状数组,括号序列法+暴力优化】

    校门外的树 描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的-- 如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作: K=1,K= ...

  10. 牛客网 牛牛数括号(DP)

    题目连接 题意 题干已经说的很清楚 给你两个括号序列,不保证合法,求有多少种不同的方法可以将两个括号序列合并成一个合法的括号序列 合并的时候不能改变各自序列原先的顺序 思路 dp[i][j] 表示s1 ...

最新文章

  1. python的csv模块的write_rows_Python3使用csv模块csv.writer().writerow()保存csv文件,产生空行的问题...
  2. 图灵七月书讯【Cassandra权威指南将在7月末上市】
  3. openstack运维实战系列(十)之nova指定compute节点和IP地址
  4. 网络埋藏了怎样的物联网智慧?
  5. 【Kotlin】Kotlin 类的继承 三 ( super 关键字使用 | super@ 外部调用父类方法 | 子类选择性调用 父类 / 接口 方法 super )
  6. Spark写Redis+Spark资源配置总结
  7. 网站页面增加一个简单的密码登录访问php网站源码
  8. Linux C socket 编程之TCP
  9. 基于Echarts5实现的动态排序柱状图
  10. Elasticsearch节点类型
  11. 记录ubuntu18.04安装QQ过程
  12. 【嵌入式06】寄存器方式和HAL库方式实现 串口通信“hello windows!”+流水灯
  13. Unity3d模型渲染灯光黑暗问题解决
  14. 【边做项目边学Android】手机安全卫士09-手机防盗界面设置向导1
  15. 5-46 新浪微博热门话题 (30分)
  16. 说一说关于破解支付宝AR红包的事
  17. Sentinel SuperPro/UltraPro Monitor v2.01
  18. Java 第5、8、9章复习
  19. 【OpenCall】ICASSP2023通用会议理解及生成挑战赛邀请函
  20. angularJS中localstorage,利用localstorage来实现其他页面获取本地缓存

热门文章

  1. linux 中eth,执行命令取出linux中eth0的IP地址
  2. Linux cp命令的内涵
  3. 微信模板消息html,微信推送模板消息,偶发出现报错errcode
  4. PDF单位尺寸与像素转换
  5. C语言程序的基本,C语言编程基础知识汇总学习,适合初学者!
  6. sql-update 用法
  7. 华为升级鸿蒙系统教程,华为升级鸿蒙系统方法汇总 华为手机各型号升级鸿蒙系统教程...
  8. 致信oa系统服务器ip,OA系统登陆考勤IP控制
  9. 图片识别之验证码识别
  10. Vue中图片裁剪的实现