代码改变世界

蓝桥杯[十二届][B组]-括号序列

  幻霞  阅读(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个,所以这里讨论的是加右括号的方案数(当然在函数里是翻转考虑左括号的)
这里翻转一下变成)))(

相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示