[NOIp模拟赛][动态规划] 简单的序列

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


惨痛教训:不要形成思维定势!!!

一看到括号序列,满脑子就往栈去了,然后啥也没想出来就爆蛋了……

实际上,我们可以将整个括号序列抽象为如下形式:

[( ightarrow 1 \ ) ightarrow -1 ]

那么一个合法的括号序列要保证:

  1. 前缀和为 (0)
  2. 每一部分的前缀和不小于 (0)

然后我们可以设 (f_{i,j}) 表示长度为 (i) 的括号序列前缀为 (j) 的合法情况有多少个,然后一个非常简单的 (DP) 就能解决问题了。

代码:

# include <iostream>
# include <cstdio> 
# define MAXN 100005
# define MAXM 2005

const long long MOD = 1e9+7;

char s[MAXN];
long long f[MAXM][MAXM];
// f[i][j] 长度为 i,前缀和为 j
// ( -> 1 ) -> -1
// 合法序列:j = 0,每个前缀和都不小于 0

int main(){
	int n, m;
	scanf("%d%d", &n, &m);
	scanf("%s", s+1);

	f[0][0] = 1;

	for(int i = 1; i <= n-m; i++){
		for(int j = 0; j <= i; j++){
			if(j > 0){
				f[i][j] += f[i-1][j-1];
				if(f[i][j] > MOD){
					f[i][j] -= MOD;
				}
			}
			if(j < i-1){
				f[i][j] += f[i-1][j+1];
				if(f[i][j] > MOD){
					f[i][j] -= MOD;
				}
			}
		}
	}

	int cur = 0, sum = 0;

	for(int i = 1; i <= m; i++){
		cur += s[i] == '(' ? 1 : -1;
		sum = std::min(sum, cur);
	}

	long long ans = 0;

	for(int i = -sum; i <= n-m; i++){
		for(int j = -sum; j <= i; j++){
			if(cur + j <= n-m-i){
				ans = (ans + f[i][j] * f[n-m-i][cur+j]) % MOD;
			}
		}
	}

	printf("%lld", ans);

	return 0;
}

切记:不要形成思维定势!!!

原文地址:https://www.cnblogs.com/Foggy-Forest/p/13741247.html