赛后感受
这场打下来有点坐牢,K题当时想假了,当时想的是组合数学+暴搜但是没有成功。没想到下来是DP,感觉有点亏,毕竟动归的本质还是组合数学,当时没有DP出来现在就来补题一下。顺便写个blog加深一下印象。
原题链接
题的大意:
给一个子串长度和原串长度。
再给的是一个子串(A) ,让求有多少个合法原串(B)。
解题思路:
DP思想:设前 i
个B里最多匹配到前 j
个A ,也就是求有多少在可能的B的方案数
这时候还需要注意仅用i j
标记的dp不一定合法,因此dp设计为三维 再设计一个K
来判断知否合法,判断思路为有 K
个左括号未被匹配:不为零,即为不合法。
状态转移过程:
现在考虑 如果在前i
个B后面加一个左括号会怎么样
得到转移方程:dp(i,j,k)->dp(i+1,j,k)
再考虑,如果A的j+1
是左括号就能匹配上,如果不是就不能匹配上
如果是
dp(i,j,k)->dp(i+1,j+1,k+1)
如果不是
dp(i,j,k)->dp(i+1,j,k+1)
如果是右括号 还需要看一下k是否不为零,即有未匹配的左括号
AC代码:
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define Buff std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int N = 205;
const int mod = 1e9 + 7;
int t, n, m;
int dp[N][N][N];
signed main()
{
Buff;
cin >> t;
while (t--)
{
cin >> n >> m;
string s;
cin >> s;
s = " " + s;
for (int i = 0; i <= m; i++)
for (int j = 0; j <= n; j++)
for (int k = 0; k <= i; k++) // k到i即可
{
dp[i][j][k] = 0;
}
dp[0][0][0] = 1; //初始化 空序列没有剩任何的括号
for (int i = 0; i <= m; i++)
{
for (int j = 0; j <= n; j++)
{
for (int k = 0; k <= i; k++)
{
if (!dp[i][j][k])
continue;
if (s[j + 1] == '(')
dp[i + 1][j + 1][k + 1] = (dp[i + 1][j + 1][k + 1] + dp[i][j][k]) % mod;//推出来的状态转移方程
else
dp[i + 1][j][k + 1] = (dp[i + 1][j][k + 1] + dp[i][j][k]) % mod;
if (k)
if (s[j + 1] == ')')
dp[i + 1][j + 1][k - 1] = (dp[i + 1][j + 1][k - 1] + dp[i][j][k]) % mod;
else
dp[i + 1][j][k - 1] = (dp[i + 1][j][k - 1] + dp[i][j][k]) % mod;
}
}
}
cout << dp[m][n][0] << endl;
}
return 0;
}