清北-Day6-regular

题目描述
给出一个只包含左括号和右括号的字符串,插入若干左右括号(可以插在任意位置)之后使得字符串长度为$ 2\times n $ 且是一个合法的括号序列。求最后能组成多少种不同的合法括号序列。
【合法的括号序列:该序列任意一个前缀的左括号数大于等于右括号数,最终左括号数等于右括号数】

输入
输入文件名为regular.in。
第一行一个数 \(n\)
第二行一个字符串(长度小于等于 $ 2\times n $ )

输出
输出文件名为regular.out。
输出一个数,表示答案 $ \pmod {10^9 + 7} $

样例输入

2
()

样例输出

2

提示
【数据说明】
对于50%的数据,$ 1 \le n \le 10 $

对于100%的数据,$ 1 \le n \le 100 $

这个题...从学术角度来看,不失为一道好题,但是从我个人感情角度来看,就是道破题,为什么?——题目具有迷惑性...导致我考虑错误的思路半天.....我一开始错误的思路就是把一共能有多少对括号算出来
然后用Catlan数求解,不过这个思路为什么错了,我还不是很清楚...
说说正解的思路吧——DP:
这里采用了三维, $ dp_{ i , j , k } $ 表示插入 $ i $ 个括号,使用了原来的 $ j $ 个括号,现在左括号比右括号多 $ k $ 个的方案数
看起来挺麻烦的是吧....确实挺麻烦,一共有四个状态转移方程——使用原序列的左括号,插入一个左括号,使用原序列的右括号,插入一个右括号,这分别是四个方程的意义
我们考虑,枚举三维状态进行转移
当我们当前有一个左括号时,我们就要采用前两种转移,枚举到一个位置并且不是末位的时候,我们当然可以选择使用一个原序列的左括号或者再插入一个左括号,显然这样一定合法
同理,当我们有一个右括号时,我们应该采用后两种转移,枚举到一个位置并且不是末位的时候,我们当然也可以选择使用一个原序列的右括号或者再插入一个右括号,显然这样也是合法的.
最后的答案自然是在 $ dp_{ i \times 2 - m , m , 0} $ 中了
代码如下:

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#define LL long long

const LL mod = 1e9 + 7 ;

LL dp[220][220][220],n,m;
char s[10000];

int main(){
	scanf ("%lld" , & n );
	scanf ("%s" , s );
	dp[0][0][0] = 1 ; m = strlen ( s ) ;
	register LL maxf = ( n << 1 ) - m ;
	for(int i = 0 ; i <= maxf ; ++ i)
		for (int j = 0 ; j <= m ; ++ j)
			for (int k = 0 ; k <= n ; ++ k){
				if ( s[j] == '(' && j < m )
					dp[ i ][ j + 1 ][ k + 1 ] = ( dp [ i ][ j ][ k ] + dp[ i ][ j + 1 ][ k + 1 ] ) % mod ;
				else dp[ i + 1 ][ j ][ k + 1 ] = ( dp [ i ][ j ][ k ] + dp[ i + 1 ][ j ][ k + 1 ] ) % mod ;
				if ( k ){
					if ( s[j] == ')' && j < m )
						dp[ i ][ j + 1 ][ k - 1 ] = ( dp [ i ][ j ][ k ] + dp[ i ][ j + 1 ][ k - 1 ] ) % mod ;
					else dp[ i + 1 ][ j ][ k - 1 ] = ( dp [ i ][ j ][ k ] + dp[ i + 1 ][ j ][ k - 1 ] ) % mod ;
				}
			}
	printf ("%lld\n" , dp[maxf][m][0] % mod );
	return 0;
}
posted @ 2018-11-05 11:21  Phecda  阅读(191)  评论(0编辑  收藏  举报

Contact with me