Codeforces Round #343 (Div. 2) Problem C——Famil Door and Brackets

题意:有一串由m个括号组成的字符串(设为s)。要在这个字符串前面和后面加上若干个括号,使得:1.字符总数为n;2.左括号跟右括号总量相等;3.任意一个前缀,他的左括号数量大于等于右括号数量。求共有多少中加的方法
思路:在字符串后面加上的括号要受到前面加上的括号的限制。如果知道了前面加上了多少个“(”与“)”,那么后面加上多少个“(”与“)”久可以确定了。
在左边加上括号的时候受这么几个条件限制:
1.题目中的条件3
2.左括号的最大数量上限为n/2-(s中左括号的数量)。
3.左括号的数量下限要保证题目中条件3在s中也能成立。
更确切的说,这三个条件能算出来的是s前面加上的“(”与“)”的数量差的范围。
s前面加上字符的个数也是有范围的,即要大于等于上面算出来的“(”与“)”数量差,加上s后数量要小于等于n。
由此推出,我们需要一个辅助的中间结果,这个结果要求出对于长度为i的字符串,满足“(”与“)”差值为j的合法的方法为多少。
这个结果可以用dp来求解。
求解出来以后,对于s前面加上的字符串,在上面求出来的合法范围内枚举。每次枚举可以知道s后面加上的字符串有多少个“(”与“)”。剩下最后一个需要解决的问题,怎么样保证在后面加上的字符串仍然合法。因为我们求出来的dp要求是从头开始,如果是在后面加上字符串,受到前面的限制貌似就不能再用这个结果。
注意到题目中要求的三个条件实际上是对称的。对任意前缀“(”大于等于“)”等价于对于任意后缀“)”大于等于“(”。
这样,知道了s后面要加上字符串的长度,以及“)”比“(”多多少个,久可以使用dp了。
代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

typedef long long ll;

const ll mod = 1000000007;

ll dp[2005][2005];
char str[100005];

int main()
{
//freopen("data.txt","r",stdin);
    memset(dp,0,sizeof(dp));
    dp[0][0] = 1;
    for(int i = 1; i <=2000;++i){
        for(int j = 0;j<=i;++j){
            if(j>=1)dp[i][j] += dp[i-1][j-1];
            if(j+1<=i) dp[i][j] += dp[i-1][j+1];
            dp[i][j] %= mod;
        }
    }
    int n,m;
    scanf("%d%d",&n,&m);
    scanf("%s",str);
    int tl=0;
    int tr = 0;
    int minl = 0;
    for(int i = 0; i < m; ++i){
        if(str[i] == '(')tl++;
        else tr++;
        minl = max(minl,tr-tl);
    }
    if(n&1){
        printf("0\n");
        return 0;
    }
    int totl = n>>1;
    int totr = n >>1;
    ll ans = 0;
    for(int i = minl;i <= totl - tl;++i){
        for(int j = 0; j <= i - minl; ++j){
            ans += dp[i+j][i-j] * dp[n - i - j - m][tl+i-tr-j];
            ans %=mod;
        }
    }
    printf("%lld\n",ans);
    return 0;
}
发布了267 篇原创文章 · 获赞 12 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/u010734277/article/details/50755772