发现牛客多校的题对我来说有点难,五个小时罚座就写出了一题,就只能看佬佬的题解补题了,我尽量按照我的理解把补题写的详细点,这样许多像我一样新手向的也能看懂了。
K. Link with Bracket Sequence I
K. Link with Bracket Sequence I
题目意思
给你一串长度为n的括号序列a,将其补全成长度为m的合法括号序列
问有多少种补全方案,答案对1e9+7取模。
分析
可以动态规划(我又想不到)
首先可以将'('看成是1,')'看成是-1,括号串序列合法就是其前缀和一直大
于等于0,且最后的和为0。
dp[i][j][k]代表将a串的前j个括号补成i个,且前缀和为k的方法数
那么动态转移方程有四种形式
① i位放置(,且s[j]!=( 或者j为0
dp[i][j][k]=dp[i][j][k]+dp[i-1][j][k-1]
② i位放置( ,且s[j]=(
dp[i][j][k]=dp[i][j][k]+dp[i-1][j-1][k-1]
③ i位放置),且s[j]!=)或j为0
dp[i][j][k]=dp[i][j][k]+dp[i-1][j][k+1]
④ i位放置),且s[j]=)
dp[i][j][k]=dp[i][j][k]+dp[i-1][j-1][k+1]
①和②的情况,由于要前缀和一直大于等于0,所以k>=1
最后就是要考虑边界 dp[0][0][0]=1
代码
#include<bits/stdc++.h>
using namespace std;
#define double long double
typedef long long ll;
const ll mod=1e9+7;
const ll inf=0x3f3f3f3f;
const double eps=1e-10;
ll dp[300][300][300];
char ch[300];
void solve()
{
ll n,m,i,j,k;
cin>>n>>m;
cin>>ch;
for(i=n;i>=1;i--) ch[i]=ch[i-1];
for(i=1;i<=m;i++)
{
for(j=0;j<=min(n,i);j++)
{
for(k=0;k<=i;k++)
{
dp[i][j][k]=0;
}
}
}
dp[0][0][0]=1;
for(i=1;i<=m;i++)
{
for(j=0;j<=min(n,i);j++)
{
for(k=0;k<=i;k++)
{
// i位放置(,且s[j]!=(
// dp[i][j][k]=dp[i][j][k]+dp[i-1][j][k-1]
if((ch[j]!='('||j==0)&&k>=1)
{
dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k-1])%mod;
}
// i位放置( ,且s[j]=(
// dp[i][j][k]=dp[i][j][k]+dp[i-1][j-1][k-1]
if(k>=1&&j>=1&&ch[j]=='(')
{
dp[i][j][k]=(dp[i][j][k]+dp[i-1][j-1][k-1])%mod;
}
// i位放置),且s[j]!=)
// dp[i][j][k]=dp[i][j][k]+dp[i-1][j][k+1]
if(ch[j]!=')'||j==0)
{
dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k+1])%mod;
}
// i位放置),且s[j]=)
// dp[i][j][k]=dp[i][j][k]+dp[i-1][j-1][k+1]
if(j>=1&&ch[j]==')')
{
dp[i][j][k]=(dp[i][j][k]+dp[i-1][j-1][k+1])%mod;
}
}
}
}
cout<<dp[m][n][0]<<endl;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}