题意
给定一个长度为\(n\)的括号序列\(a\)(不一定是合法的)。
现在要使用\(a\)构造一个长度为\(m\)的合法括号序列\(b\),其中\(a\)是\(b\)的子序列(不一定连续)
求方案数。
题目链接:https://ac.nowcoder.com/acm/contest/33187/K
数据范围
\(1 \leq n \leq m \leq 200\)
思路
这道题很容易想到使用DP,并且通过时间复杂度推算,应该为三维DP。
定义\(f(i, j, k)\)表示答案串已经有\(i\)个字符,括号序列\(a\)中的前\(j\)个字符是答案串的子序列,左括号比右括号数量多\(k\)的方案数。
下面考虑转移方程。考虑答案串下一个要放的括号,即第\(i+1\)个字符。
- 如果放左括号,如果\(a\)的第\(j+1\)个字符为左括号,那么就能够匹配上,则\(f(i+1,j+1,k+1) = f(i+1,j+1,k+1)+f(i,j,k)\)
- 如果放左括号,如果\(a\)的第\(j+1\)个字符不为左括号,那么不能匹配,则\(f(i+1,j,k+1) = f(i+1,j,k+1)+f(i,j,k)\)
- 如果放右括号,如果\(a\)的第\(j+1\)个字符为右括号,那么就能够匹配上,则\(f(i+1,j+1,k-1) = f(i+1,j+1,k-1)+f(i,j,k)\)
- 如果放右括号,如果\(a\)的第\(j+1\)个字符不为右括号,那么不能匹配,则\(f(i+1,j,k-1) = f(i+1,j,k-1)+f(i,j,k)\)
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 210, mod = 1e9 + 7;
int n, m;
char s[N];
ll f[N][N][N];
int main()
{
int T;
scanf("%d", &T);
while(T --) {
scanf("%d%d", &n, &m);
scanf("%s", s + 1);
for(int i = 0; i <= m; i ++) {
for(int j = 0; j <= n; j ++) {
for(int k = 0; k <= m; k ++) {
f[i][j][k] = 0;
}
}
}
f[0][0][0] = 1;
for(int i = 0; i < m; i ++) {
for(int j = 0; j <= min(i, n); j ++) {
for(int k = 0; k <= i; k ++) {
if(k < m && (j == n || s[j + 1] != '(')) {
f[i + 1][j][k + 1] = (f[i + 1][j][k + 1] + f[i][j][k]) % mod;
}
if(k < m && j < n && s[j + 1] == '(') {
f[i + 1][j + 1][k + 1] = (f[i + 1][j + 1][k + 1] + f[i][j][k]) % mod;
}
if(k && (j == n || s[j + 1] != ')')) {
f[i + 1][j][k - 1] = (f[i + 1][j][k - 1] + f[i][j][k]) % mod;
}
if(k < m && j < n && s[j + 1] == ')') {
f[i + 1][j + 1][k - 1] = (f[i + 1][j + 1][k - 1] + f[i][j][k]) % mod;
}
}
}
}
printf("%lld\n", f[m][n][0]);
}
return 0;
}