CSP-S2021 T2 括号序列 题解
\(这道题考场上没拿下真的很不应该。看数据范围\) n <= 500 \(就知道是一道裸的区间dp题\)
\(转移方程甚至直接按照题目要求来模拟就可以了\)
\(首先先加上一三两种情况的答案 然后是子区间的一三两种情况的方案数乘上第二种情况的方案数\)
\(对于第二种情况要多考虑一下 我们只计算*号或者?号都在左侧的就没问题了 保证不重复\)
\(剩下没什么大问题。细节说多也不多,想想也就知道了。\)
code:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 510;
const int mod = 1e9 + 7;
char s[N];
inline int read()
{
char ch = getchar();int f = 0,x = 0;
while(ch > '9' || ch < '0') f |= (ch == '-'),ch = getchar();
while(ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch - '0'),ch = getchar();
if(f) x = -x;
return x;
}
inline void add(int &x,int y)
{
x += y;
if(x >= mod) x -= mod;
}
int n,K;
int dp[N][N],dp2[N][N],dp3[N][N];
int c[N];
bool check(int l,int r)
{
return c[r] == c[l - 1];
}
int main()
{
n = read(),K = read();
scanf("%s",s + 1);
for(register int i = 1; i <= n; ++ i)
c[i] = c[i-1] + (s[i] == '(' || s[i] == ')');
for(register int len = 2; len <= n; ++ len)
for(register int i = 1; i + len - 1 <= n; ++ i)
{
int j = i + len - 1;
if(s[i] == '*' || s[j] == '*' || s[i] == ')' || s[j] == '(')
; //直接无视不合法的情况
else
{
if(len <= K + 2 && check(i + 1,j - 1))
dp[i][j] = 1;
//1;
add(dp[i][j],dp[i + 1][j - 1]);
for(register int k = 1; k <= K && k <= len - 4; ++ k)
{
if(check(i + 1,i + k))
add(dp[i][j],dp[i + k + 1][j - 1]);
else break;
}
for(register int k = 1; k <= K && k <= len - 4; ++ k)
{
if(check(j - k,j - 1))
add(dp[i][j],dp[i + 1][j - k - 1]);
else break;
}
//3
//cout << dp[i][j] << endl;
dp3[i][j] = dp[i][j];
for(register int k = i + 1; k < j; ++ k)
add(dp[i][j],1LL * dp3[i][k - 1] * dp2[k][j] % mod);
}
//2
//cout << dp[i][j] << endl;
for(register int k = 0; k <= K && k <= len - 2; ++ k)
if(check(i,i + k - 1))
add(dp2[i][j],dp[i + k][j]);
else
break; // **()()...
}
printf("%d", dp[1][n]);
return 0;
}