[dp] Jzoj P5804 简单的序列
题解
- 我们可以把左括号看成+1,右括号看成-1
- 那么一个合法的括号序列就是左括号>=右括号
- 设f[i][j]为长度为i的序列 左括号比右括号多j个 的方案数
- 状态转移方程明显就是:f[i-1][j-1]+f[i-1][j+1]
- 那么考虑如何求最终的方案数
- 首先要满足左括号多于右括号,和 |p| + |s| + |q| = n
- 每次枚举一个i表示前面的p序列的长度,再枚举一个j表示p序列左括号比右括号多的个数
- 那么就是f[i][j]*f[n-m-i][j+tot](tot为原来序列中左括号比右括号多的个数)
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 const long long mo=1e9+7; 7 int n,m,tot,ltot; 8 char s[100010]; 9 long long ans,f[2010][2010]; 10 int main() 11 { 12 freopen("bracket.in","r",stdin); 13 freopen("bracket.out","w",stdout); 14 scanf("%d%d",&n,&m); 15 scanf("%s",s); 16 for (int i=0;i<m;i++) 17 { 18 if (s[i]=='(') tot++; else tot--; 19 if (i==0) ltot=tot; else ltot=min(tot,ltot); 20 } 21 f[0][0]=1; 22 for (int i=1;i<=n-m;i++) 23 for (int j=0;j<=i;j++) 24 if (j==0) f[i][j]=f[i-1][j+1]; 25 else f[i][j]=(f[i-1][j-1]+f[i-1][j+1])%mo; 26 for (int i=0;i<=n-m;i++) 27 for (int j=0;j<=i;j++) 28 if (j+tot<=n-m&&j+ltot>=0) 29 (ans+=f[i][j]*f[n-m-i][j+tot])%=mo; 30 printf("%lld",ans); 31 return 0; 32 }