简单的序列 简单的动态规划

Description
从前有个括号序列 s,满足 |s| = m。你需要统计括号序列对 (p, q) 的数量。
其中 (p, q) 满足 |p| + |s| + |q| = n,且 p + s + q 是一个合法的括号序列。
Input
从文件 bracket.in 中读入数据。
第一行两个正整数 n, m。
第二行一个长度为 m 的括号序列,表示 s。
Output
输出到文件 bracket.out 中。
输出一行一个整数,表示符合条件的 (p, q) 的数量对 10^9 + 7 取模的值。
Sample Input
【样例 1 输入】
4 1 (
【样例 2 输入】
4 4 (())
【样例 3 输入
4 3 (((
Sample Output
【样例 1 输出】
4
【样例 2 输出】
1
【样例 3 输出】
0
Data Constraint
对于 10% 的数据,n ≤ 20;
对于 25% 的数据,n ≤ 200;
对于另外 5% 的数据,n = m;
对于 55% 的数据,n − m ≤ 200;
对于 100% 的数据,1 ≤ m ≤ n ≤ 10^5, n − m ≤ 2000。

很显然,这是一道..勉强算是动态规划吧。
首先,我们需要提前跑一边动态规划,并且确定一系列的状态,来对p进行预处理。
例如,”(()(“和”()((“虽然是两种不同的方案,但是后面都是接两个”)”,所以在运用的时候没有区别。
所以,我们可以巧妙的将”(“看作是1,”)”看作是-1,就可以得到一个状态 f[i][j] 表示i个括号所达到的值为j。
因为我们知道,我们在研究p的时候,如果出现了右括号大于左括号的情况,就无法匹配,因为p前面已经没有括号和剩下的右括号匹配了。所以我们需要保证j≥0,就可以确保我们求的状态始终不会出现”)(“这类情况,因为”)”这种情况达到的是负值,我们不会让它从负值转移过来。f[2][0]=f[1][1],但绝不会让它出现这样的转移f[2][0]=f[1][-1],这样,就能够顺利排除所有右括号多余左括号的情况。
然后,除去左右括号相等的特殊情况,对于普遍的情况,我们可以直接得到,增添左括号的状态转移,增添右括号的状态转移,所以,有f[i][j]=f[i-1][j-1]+f[i-1][j+1]。
其实,这个状态我们可以直接把左括号看作右括号,把右括号看作左括号,就可以同样看作是q的状态,因为q的状态不允许左括号多于右括号,正好与p状态相反。
因此,接下来的推断,只需要使用这个f[][]数组,就可以解决问题了。
然后,我们只要再添上一个特判,保证p这个状态一定能和s的左端某个状态匹配,也就是找出s左端连续的某段所失配的右括号最大,然后从这个值开始枚举p状态,就可以很快解决问题了。
当然,理论上q也需要这样的枚举,但是因为q的状态直接由p、s状态直接确定,所以就不需要对它进行枚举、判断了。
代码如下:

#include <iostream>
#include <cstdio>

using namespace std;

const int maxn=1000000007;
int n,m,minn;
int x[100010];
long long ans;
int f[2010][2010];

void read()
{
    char c=getchar();
    while ((c!='(')&&(c!=')'))
        c=getchar();
    while ((c=='(')||(c==')'))
    {
        if (c=='(')
            x[++m]=1;
        else
            x[++m]=-1;
        c=getchar();
    }
    return ;
}

int main()
{
    scanf("%d%d",&n,&m);
    m=0;
    read();
    f[0][0]=f[1][1]=1;
    for (int i=2;i<=n-m;i++)
    {
        f[i][0]=f[i-1][1];
        for (int j=1;j<=n-m;j++)
            f[i][j]=(f[i-1][j-1]+f[i-1][j+1])%maxn;
    }
    int now=0;
    for (int i=1;i<=m;i++)
    {
        now+=x[i];
        if (now<minn)
            minn=now;
    }
    for (int i=(-1*minn);i<=n-m;i++)
        for (int j=(-1*minn);j<=i;j++)
        {
            int len=n-m-i,mark=now+j;
            if (mark>=0)
                ans=((long long )f[len][mark]*f[i][j]+ans)%maxn;
        }
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/CutieDeng/article/details/81974230
今日推荐