[CSP-S模拟测试]:简单的序列(DP)

题目描述

从前有个括号序列$s$,满足$|s|=m$。你需要统计括号序列对$(p,q)$的数量。
其中$(p,q)$满足$|p|+|s|+|q|=n$,且$p+s+q$是一个合法的括号序列。


输入格式

从文件$bracket.in$中读入数据。第一行两个正整数$n,m$。
第二行一个长度为$m$的括号序列,表示$s$。


输出格式

输出到文件$bracket.out$中。
输出一行一个整数,表示符合条件的$(p,q)$的数量对$10^9+7$取模的值。


样例

样例输入1:

4 1 (

样例输出1:

4

样例输入2:

4 4 (())

样例输出2:

1

样例输入3:

4 3 (((

样例输出3:

0


数据范围与提示

对于$10\%$的数据,$n\leqslant 20$;
对于$25\%$的数据,$n\leqslant 200$;
对于另外$5\%$的数据,$n=m$;
对于$55\%$的数据,$n−m\leqslant 200$;
对于$100\%$的数据,$1\leqslant m\leqslant n\leqslant 10^5,n−m\leqslant 2,000$。


题解

首先,不妨设左括号为$+1$,右括号为$-1$,那么要满足最后和为$0$,且所有前缀不小于$0$。

对于原串处理时的最小前缀如果小于$0$,则必须在原串前加左括号使之合法。

考虑$DP$,设$dp[i][j]$表示长度为$i$,总和为$j$的括号序列数量,那么$p$的方案数为$dp[i][j]$,$q$的方案数为$dp[n-m-i][j-a]$($a$为原串的总和)。

将其乘积相加即为答案。

时间复杂度:$\Theta((n-m)^2)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int n,m;
char ch[100001];
int res,minn;
long long dp[2001][2001],ans;
int main()
{
	scanf("%d%d%s",&n,&m,ch+1);
	for(int i=1;i<=m;i++)
	{
		if(ch[i]=='(')res++;
		else res--;
		minn=min(minn,res);
	}
	dp[0][0]=1;
	for(int i=1;i<=n-m;i++)
		for(int j=0;j<=i;j++)
		{
			dp[i][j]=dp[i-1][j+1];
			if(j)dp[i][j]=(dp[i][j]+dp[i-1][j-1])%mod;
		}
	for(int i=0;i<=n-m;i++)
		for(int j=0;j<=i;j++)
			if(j+res<=n-m&&j+minn>=0)
				ans=(ans+dp[i][j]*dp[n-m-i][res+j]%mod)%mod;
	printf("%lld",ans);
	return 0;
}

rp++

posted @ 2019-10-14 11:31  HEOI-动动  阅读(239)  评论(0编辑  收藏  举报