牛客普及组-合法括号序列
题目大意
键盘上有左括号(,右括号),和退格键-,共三个键。
牛牛希望按键n次,使得输入的字符串恰好一个合法的括号序列。
每按一次左括号(,字符串末尾追加一个左括号(
每按一次右括号),字符串末尾追加一个右括号)
每按一次退格键-,会删掉字符串的最后一个字符,
特别的,如果字符串为空,牛牛也可以按退格,但是什么都不会发生。
合法括号序列的定义和上一场比赛中的C题是一样的
https://www.nowcoder.com/acm/contest/164/C
输出方案数对p取模,注意p可能不是质数。
注:只要按键方法不同,就是不同的方案,即使得到的序列一样。
此题有一个比较麻烦的地方就是需要把删除的贡献也考虑在内
不妨将当前括号序列长度考虑在内
设 g[i][j] 表示进行了 i 次操作后序列长度为 j 的方案数
转移也很好想 : g[i][j] = g[i -1][j - 1] + g[i - 1][j + 1] * 2
乘以 2 就是填入左 / 右括号,j = 0 时把 j - 1 改成 0
由于统计答案时统计的是合法括号序,就再 dp 出长度为 i 时的合法括号序即可
状态设计为 f[i][j] 表示当前长度为 i ,还有 j 个左括号未匹配
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cctype> #include<cstdio> using namespace std; typedef long long ll; const int MAXN = 1005; int n; ll f[MAXN][MAXN], g[MAXN][MAXN]; ll p, ans; int main() { scanf("%d%lld", &n, &p); f[0][0] = 1ll; for(int i = 1; i <= n; ++i) { f[i][0] = f[i - 1][1]; for(int j = 1; j <= i; ++j) { f[i][j] = (f[i][j] + f[i - 1][j + 1] + f[i - 1][j - 1]) % p; } } g[0][0] = 1ll; for(int i = 1; i <= n; ++i) { for(int j = 0; j <= i; ++j) { g[i][j] = (g[i - 1][max(j - 1, 0)] + g[i - 1][j + 1] * 2) % p; } } ans = g[n][0]; for(int j = 2; j <= n; j += 2) { ans = (ans + f[j][0] * g[n][j]) % p; } printf("%lld\n", ans); return 0; }
因为合法的括号序长度一定是偶数,所以枚举长度也枚举偶数
禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载
,用户转载请注明出处:https://www.cnblogs.com/xcysblog/