• [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;
    }
    

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

  • 相关阅读:
    bzoj 1040: [ZJOI2008]骑士
    CF Playrix Codescapes Cup Problems Analysis
    C++ 指针[转+原创]
    高斯消元
    NOIP模拟
    线段树合并
    After ZJOI2017 day2
    ZJOI 2017 day2 4.27
    ZJOI 2017 二试 day1 4.26
    ZJOI 2017 二试 day0
  • 原文地址:https://www.cnblogs.com/Foggy-Forest/p/13741247.html
Copyright © 2020-2023  润新知