[SMOJ1989]圆括号
来源:互联网 发布:seo网络优化培训 编辑:程序博客网 时间:2024/06/10 22:24
这一题很明显是 DP,但是状态的表示就有方法了。主要是要在解决括号序列的合法匹配问题上做功夫。
首先明确这样的一条基本事实:在一个合法的括号序列中,对于任意的
我在比赛时候用了非常繁琐的状态,因为我的思路非常简单粗暴,记
也就是说我实际上是用了到目前为止左括号个数减去右括号个数保证状态的合法性,则最终的答案应该为
边界条件:f[0][0][0][1] = 1;
转移就分类讨论一下,当前状态可能是取
因为取的是
如果是左括号,那么新增了一个待配对的左括号,之前就应该是
而如果是右括号,则为之前某个待配对的左括号配了对,之前就应该是
这样,总的时间复杂度为
我在这里需要反思一点,即
参考代码:
#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>using namespace std;const int MAXL = 50 + 10;const long long MOD = 1000000007LL; char X[MAXL], Y[MAXL];int Xlen, Ylen;long long f[MAXL][MAXL][MAXL << 1][5];bool isok() { if (X[1] != '(' && Y[1] != '(' || X[Xlen] != ')' && Y[Ylen] != ')') return false; int cnt[256] = {0}; for (int i = 1; i <= Xlen; i++) ++cnt[X[i]]; for (int i = 1; i <= Ylen; i++) ++cnt[Y[i]]; return cnt['('] == cnt[')'];}int main(void) { freopen("1989.in", "r", stdin); freopen("1989.out", "w", stdout); int T; scanf("%d", &T); while (T--) { memset(f, 0, sizeof f); scanf("%s%s", X + 1, Y + 1); Xlen = strlen(X + 1); Ylen = strlen(Y + 1); if (((Xlen + Ylen) & 1) || !isok()) { puts("0"); continue; } f[0][0][0][1] = 1LL; for (int i = 0; i <= Xlen; i++) { int ic = X[i] - '('; for (int j = 0; j <= Ylen; j++) { int jc = Y[j] - '('; for (int k = 1; k <= i + j; k++) { if (i > 0 && ic == 0) f[i][j][k][0] += f[i - 1][j][k - 1][0] + f[i - 1][j][k - 1][1]; f[i][j][k][0] %= MOD; if (j > 0 && jc == 0) f[i][j][k][0] += f[i][j - 1][k - 1][0] + f[i][j - 1][k - 1][1]; f[i][j][k][0] %= MOD; } for (int k = 0; k < i + j; k++) { if (i > 0 && ic == 1) f[i][j][k][1] += f[i - 1][j][k + 1][0] + f[i - 1][j][k + 1][1]; f[i][j][k][1] %= MOD; if (j > 0 && jc == 1) f[i][j][k][1] += f[i][j - 1][k + 1][0] + f[i][j - 1][k + 1][1]; f[i][j][k][1] %= MOD; } } } printf("%lld\n", f[Xlen][Ylen][0][1]); } return 0;}
话说回来,其实完全可以进一步优化,赛后讲评的时候 Monad 上去讲了
首先要发现,最后一维可以省去,考虑当前字符是左括号还是右括号其实是一样的,可以统一处理,在决策的过程中不需要去考虑它(仔细体会一下这句话)。
当然,这样只是在状态上少了 0/1,对于时间来说没什么大变化。既然可以
第三维也是可以省去的。
其实上面状态中为了保证合法性而添加的一维表明我的潜意识已经有点接近正解了,但是我只看到了表面。
这里其实可以用
可以通过维护一个前缀和之类的东西,快速算出
如果合法(左括号个数大于等于右括号个数),那么 f[i][j] += f[i-1][j]+f[i][j-1]
;否则 f[i][j] = 0
。
最后的答案为
最后要注意,可以预先特判一些不合法的情况,如
Ghastlcon 大佬的代码(%%%%%):
#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#include <string>#define N 70#define MOD 1000000007using namespace std;long long f[N][N];int Count(string &x, int p, char c){ int i, j; for(i = j = 0;i <= p;i ++) j += x[i] == c; return j;}int main(){ int t; string x, y; int i, j; freopen("1989.in" , "r", stdin ); freopen("1989.out", "w", stdout); cin >> t; while(t --) { cin >> x >> y; memset(f, 0, sizeof(f)); for(i = 0, f[0][0] = 1;i <= (signed)x.size();i ++) for(j = 0;j <= (signed)y.size();j ++) if(Count(x, i - 1, ')') + Count(y, j - 1, ')') <= Count(x, i - 1, '(') + Count(y, j - 1, '(')) f[i][j] = (f[i][j] + ((i ? f[i - 1][j] : 0) + (j ? f[i][j - 1] : 0)) % MOD) % MOD; cout << f[x.size()][y.size()] * (Count(x, i - 1, ')') + Count(y, j - 1, ')') == Count(x, i - 1, '(') + Count(y, j - 1, '(')) << endl; } return 0;}
- [SMOJ1989]圆括号
- 圆括号
- 圆括号匹配
- 圆括号匹配
- POJ 1068 圆括号编码
- 正则表达式中的圆括号
- create table用圆括号()
- 检查圆括号是否匹配
- leetcode【第三周】:输出圆括号
- Python 正则表达式 或者(|) 圆括号
- 17、ES6 圆括号的问题
- javascript匿名函数结尾处圆括号含义
- 正则表达式中的圆括号的作用
- Python 元组与圆括号使用风格
- js 两个圆括号 自调用 闭包
- [leetcode]Generate Parentheses 生成圆括号 python实现
- [Leetcode-22]Generate Parentheses 生成圆括号
- 栈的应用1--圆括号平衡
- JAVA IO其他类
- PAT题解——1083. List Grades (25)
- VMware vSphere Web Services SDK编程指南(六)- 6 Datacenter Inventory
- Codefoces 828C
- 点击按钮事件穿透
- [SMOJ1989]圆括号
- C语言接口实现弗洛伊德算法
- Java异常处理基础
- 查看jvm内存使用命令
- bootstrap系列之十七缩略图和警告框
- node-sass 的安装和使用
- 怎样抓住机会
- 编译时间太长?跟我学对症下药!
- pandas官方文档cookbook(5)中MissValue&groupby翻译