蓝桥杯[十二届][B组]-括号序列
2022-03-29 00:57 幻霞 阅读(376) 评论(0) 编辑 收藏 举报题目来自蓝桥杯练习系统
代码链接:https://blog.csdn.net/yanweiqi1754989931/article/details/123093179
这一题在思路不清楚的情况下相当难理解和解决,包括代码,解的话一开始笔者就没什么思路,想出来的方案要么超时要么难以操作
附上代码和解析的思路,希望能帮到和笔者一样被严重困扰的朋友
#include <bits/stdc++.h> #define int long long using namespace std; // 最大长度和模 const int N = 5010, MOD = 1e9 + 7; // dp[i][j]代表到第i个符号, 左括号比右括号多i个的添加方案(添加的是左括号) int dp[N][N]; inline int calc(string s) { // 长度 int len = s.size(); // 初始化 memset(dp, 0, sizeof dp); // 读到位置为0时左括号比右括号多0个的解决方案数量为1 dp[0][0] = 1; // 依次枚举每个位置 for(int i = 1; i <= len; i++) { // i-1转换为索引 if(s[i - 1] == '(') { // 依次左比右多1~len个的情况 for(int j = 1; j <= len; j++) { // 继承上一个的状态,因为读入的是左括号,所以不能加左括号了 dp[i][j] = dp[i - 1][j - 1]; } } else { // 这里处理了j=0的情况,在下面的循环中如果j=0,j-1会越界
// 其实这里表示的情况是到第i个符号,左括号小于等于右括号的情况),()), ,(())这类情况
// 此时的方案数=上一个相同情况下的方案数+左括号多一个的状态(多的状况是去补全当前添加的右括号)
// 比如,空 ,(),,())
dp[i][0] = (dp[i - 1][0] + dp[i - 1][1]) % MOD;
// 如果为右括号,考虑添加左括号
// dp[i][j-1]是dp[i][0]~dp[i][j-1]的和,代表了之前的有效解集(其实j要小于i/2 否则你会发现它的逻辑意义很奇怪,只不过这里给它延伸了一下),同时,
// dp[i][0] 到dp[i][j-1]的和其实后面的没有什么意义,只是辅助运算,从结果看我们只取了第一个不为0的解,也就是说只要计算的dpij第一个不为0(即dp[i-1][0]和dp[i-1][1])
// dp[i-1][j+1]是在上一次的情况下,模拟左括号多一个的情况,这样刚好和当前的右括号构成合法解 for(int j = 1; j <= len; j++) { dp[i][j] = (dp[i - 1][j + 1] + dp[i][j - 1]) % MOD; } } } // 调试的输出 cout<<s<<endl; cout<<"j: "; for(int j=0;j<=len;j++){ printf("%4lld ",j); } cout<<endl; for(int i = 0; i <= len; i++) { cout<<i<<": "; for(int j = 0; j <= len; j++) { printf("%4lld ",dp[i][j]); } cout<<endl; } for(int i = 0; i <= len; i++) { // 返回第一个不为零的值,理论上不可能是无解的,为什么取第一个不为0的是因为 存在((((这种情况 // dp4 0 的值为0的,也就是不能再加左括号了 if(dp[len][i]) return dp[len][i]; } // 否则返回负一 return -1; } // inline void solve() { // 输入 string s; cin >> s; // 镜像一个 string ss = s; // 翻转镜像 reverse(ss.begin(), ss.end()); // 遍历每一个位置,如果是左括号就变成右括号,如果是右括号就变为左括号,相当于翻转 for(int i = 0; i < ss.size(); i++) { ss[i] = (ss[i] == '(') ? ')' : '('; } // 最终解为两种情况乘积对mod取模 :ss其实是s的镜像比如s:(((),ss:)(((->)))( // 乘积的原理:形如)(((,此时必然要添加一个左括号以补全左边的右括号,只有一种补全方案,所以1*,而添加右括号的方式有5种,所以是5* // 所需要添加的左右括号方案便是1*5 cout << calc(s) * calc(ss) % MOD << endl; } signed main() { solve(); return 0; }
笔者回来复习,叒叒叒叒叒叒叒看不懂代码了。
但是隐约记得一些东西,现在讨论一下这个
)(((
注释中说添加右括号的方案数为5,这个比较好解释
首先它的方案数等价于(((,因为左边必须要有一个()
那么(((的方案数,(时为1,((时为()(),(()),
(((时为 1.()()(),2.()(()) 3.(())() 4.((())),5.(()())
在这些左边再加一个()结果还是5个,所以这里讨论的是加右括号的方案数(当然在函数里是翻转考虑左括号的)
这里翻转一下变成)))(
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!