CF149D Coloring Brackets

CF149D Coloring Brackets

题目

题目描述

Once Petya read a problem about a bracket sequence. He gave it much thought but didn’t find a solution. Today you will face it.

You are given string s s s . It represents a correct bracket sequence. A correct bracket sequence is the sequence of opening ("(") and closing (")") brackets, such that it is possible to obtain a correct mathematical expression from it, inserting numbers and operators between the brackets. For example, such sequences as “(())()” and “()” are correct bracket sequences and such sequences as “)()” and “(()” are not.

In a correct bracket sequence each bracket corresponds to the matching bracket (an opening bracket corresponds to the matching closing bracket and vice versa). For example, in a bracket sequence shown of the figure below, the third bracket corresponds to the matching sixth one and the fifth bracket corresponds to the fourth one.


You are allowed to color some brackets in the bracket sequence so as all three conditions are fulfilled:

  • Each bracket is either not colored any color, or is colored red, or is colored blue.
  • For any pair of matching brackets exactly one of them is colored. In other words, for any bracket the following is true: either it or the matching bracket that corresponds to it is colored.
  • No two neighboring colored brackets have the same color.

Find the number of different ways to color the bracket sequence. The ways should meet the above-given conditions. Two ways of coloring are considered different if they differ in the color of at least one bracket. As the result can be quite large, print it modulo 1000000007 1000000007 1000000007 ( 1 0 9 + 7 10^{9}+7 109+7).

输入格式

The first line contains the single string s s s ( 2 ≤ ∣ s ∣ ≤ 700 2\le|s|\le700 2s700 ) which represents a correct bracket sequence.

输出格式

Print the only number — the number of ways to color the bracket sequence that meet the above given conditions modulo 1000000007 1000000007 1000000007 ( 1 0 9 + 7 10^{9}+7 109+7 ).

题意翻译

Petya遇到了一个关于括号序列的问题: 给定一个字符串S,它代表着正确的括号序列,即(“(”)与 (“)”)是匹配的。例如:“(())()” 和 “()”是正确的,“)()”与“(()”则不是正确的。 在正确的括号序列中,一个左边的括号一定是匹配一个右边的括号(反之亦然)。例如,在下图中,第 3 个括号匹配第 6 个括号,第 4 个括号匹配第 5 个括号。

现在你需要对一个正确的括号序列做涂色操作,严格满足以下三个条件:

1、每个括号要么不涂色,要么涂红色,要么涂蓝色。

2、一对匹配的括号需要且只能将其中一个涂色。

3、相邻的括号不能涂上同一种颜色(但是可以都不涂颜色)。

求:给整个括号序列涂上颜色的方案数,答案可能比较大,对 1000000007 取模。

输入输出样例
输入 #1
(())
输出 #1
12
输入 #2
(()())
输出 #2
40
输入 #3
()
输出 #3
4
说明/提示

Let’s consider the first sample test. The bracket sequence from the sample can be colored, for example, as is shown on two figures below.

在这里插入图片描述在这里插入图片描述The two ways of coloring shown below are incorrect.
在这里插入图片描述在这里插入图片描述

原题链接

题解

思路

这道题限制有些多, 每种情况都有不同的转换方法, 所以我们把一段正确的括号序列分为9种情况, 分别对应左边与右边为各种颜色的情况。如果某一段区间不是正确的括号序列, 则这一段染成正确括号序列的方案总数就只能为0, 如果这是一段正确序列, 那么我们还需要讨论一下, 如果这个序列两端的括号是一对, 那么我们就可以直接将外层的每种情况都用去掉两端后的区间两端允许情况的和来表示。如果两端不是匹配括号, 就可以直接找到其中一个分界点, 然后把其分成两个正确的区间, 然后再将两者所有可能的情况各自方案总数相乘, 再加起来。这里不找所有分界点是因为一个分界点就可以把所有情况表示出来, 因为两个序列都是表示的所有情况, 所以它们合起来就是整个区间的所有情况了

运用算法
区 间 d p 区间dp dp

代码:

#include <cstdio>
#include <algorithm>
#include <stack>
#include <cstring>
using namespace std;

#define MAXN 700

const long long INF = 1e9 + 7;
long long dp[13][MAXN + 5][MAXN + 5];
int s[MAXN + 5];
char c[MAXN+ 5];

long long f (int l, int r, int x, int y){
	long long sum = 0;
	
	for (int i = 0; i < 3; i++){//枚举左半边的右端颜色 
		long long ans = 0;
		
		for (int j = 0; j < 3; j ++){//枚举右半边的左端颜色 
			if (i == j && i != 0){//不能颜色相等, 且都染了色 
				continue;
			}
			
			ans += dp[j * 3 + y][s[l] + 1][r];//成立就累加到ans 
			ans %= INF;
		}
		
		sum += dp[x * 3 + i][l][s[l]] * ans;//再加到sum中 
		sum %= INF;
	}
	
	return sum;
}
long long g (int l, int r, int x, int y){
	long long sum = 0;
	
	for (int i = 0; i < 3; i++){//枚举左部分颜色 
		for (int j = 0; j < 3; j++){//枚举右部分颜色 
			if ((i == x && i != 0) || (j == y && j != 0)){//如果i, j其中有一者和其挨着的两端相同且不全为无色就不考虑 
				continue;
			}
			
			sum += dp[i * 3 + j][l + 1][r - 1];//累加到sum中 
			sum %= INF;
		}
	}
	
	return sum;
}

int main(){
	int n;
	stack<int> S;
	
	scanf ("%s", c + 1);
	
	n = strlen (c + 1);
	
	for (int i = 1; i <= n; i++){//提前求出每个位置对应的另一半的位置 
		if (c[i] == '('){
			S.push(i);
		}
		else{
			s[S.top()] = i;
			s[i] = S.top();
			S.pop();
		}
	}
	
	for (int i = 1; i <= n; i ++){
		if (s[i] - i == 1){
			dp[1][i][s[i]] = dp[2][i][s[i]] = dp[3][i][s[i]] = dp[6][i][s[i]] = 1;
			//顺序表示所有情况, 可以在后面更加快速的表示左右端点颜色 
			/*
			*0:左无, 右无 
			*1:左无, 右红 
			*2:左无, 右蓝
			*3:左红, 右无 
			*4:左红, 右红
			*5:左红, 右蓝
			*6:左蓝, 右无 
			*7:左蓝, 右红
			*8:左蓝, 右蓝
			*/
		}
	}
	
	for (int i = 4; i <= n; i += 2){
		for (int l = 1; l <= n - i + 1; l++){
			int r = l + i - 1;
			
			//0:无色 1:红色 2:蓝色 
			if (c[l] == '(' && c[r] == ')' && s[l] != r && s[l] < r && s[r] > s[l]){//如果这个可能是正确序列, 并且两端不是一队括号 
				for (int k = 0 ; k < 9; k++){//更新每种情况 
					dp[k][l][r] = f(l, r, k / 3, k % 3);//观察表示, 容易发现k / 3既是左端点颜色, k % 3既是右端点颜色 
				}
			}
			else if (s[l] == r){//如果两端的括号是一对 
				//分别更新一端染一端不染的4种情况 
				dp[1][l][r] = g(l, r, 0, 1); 
				dp[2][l][r] = g(l, r, 0, 2);
				dp[3][l][r] = g(l, r, 1, 0);
				dp[6][l][r] = g(l, r, 2, 0);
			}
		}
	}
	
	long long sum = 0;
	
	for (int i = 0; i < 9; i++){//结果可能是每种情况, 累加就行 
		sum += dp[i][1][n];
		sum %= INF;
	}
	
	printf ("%d", sum);
}

后来打的一个更好理解的

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
using namespace std;

#define MAXN 800
#define INF 1000000007

char c[MAXN + 5];
int s[MAXN + 5];
long long dp[3][3][MAXN + 5][MAXN + 5];

bool check(int l, int r) {
    if (c[l] == ')' || c[r] == '(') {
        return 0;
    }

    int x = l;

    while (x < r) {
        if (c[x] != '(') {
            return 0;
        }
        x = s[x] + 1;
    }
    if (x != r + 1) {
        return 0;
    } else {
        return 1;
    }
}

int main() {
	int n;

    scanf("%s", c + 1);
    n = strlen(c + 1);

    stack<int> S;

    for (int i = 1; i <= n; i++) {
        if (c[i] == '(') {
            S.push(i);
        } else {
            s[S.top()] = i;
            s[i] = S.top();
            S.pop();
        }
    }
    for (int i = 2; i <= n; i += 2) {
        for (int j = 1; j <= n - i + 1; j++) {
            int l = j, r = j + i - 1;

            if (!check(l, r)) {
                continue;
            }
            if (s[l] == r) {//第一维表示左边颜色, 第二维表示右边颜色
                if (l + 1 == r) {//0:无色 1:红色 2:蓝色
                    dp[1][0][l][r] = 1;
                    dp[0][1][l][r] = 1;
                    dp[2][0][l][r] = 1;
                    dp[0][2][l][r] = 1;

                    continue;
                }
                dp[1][0][l][r] += dp[0][0][l + 1][r - 1];
                dp[1][0][l][r] += dp[0][1][l + 1][r - 1];
                dp[1][0][l][r] += dp[0][2][l + 1][r - 1];
                dp[1][0][l][r] += dp[2][0][l + 1][r - 1];
                dp[1][0][l][r] += dp[2][1][l + 1][r - 1];
                dp[1][0][l][r] += dp[2][2][l + 1][r - 1];

                dp[2][0][l][r] += dp[0][0][l + 1][r - 1];
                dp[2][0][l][r] += dp[0][1][l + 1][r - 1];
                dp[2][0][l][r] += dp[0][2][l + 1][r - 1];
                dp[2][0][l][r] += dp[1][0][l + 1][r - 1];
                dp[2][0][l][r] += dp[1][1][l + 1][r - 1];
                dp[2][0][l][r] += dp[1][2][l + 1][r - 1];

                dp[0][1][l][r] += dp[0][0][l + 1][r - 1];
                dp[0][1][l][r] += dp[1][0][l + 1][r - 1];
                dp[0][1][l][r] += dp[2][0][l + 1][r - 1];
                dp[0][1][l][r] += dp[0][2][l + 1][r - 1];
                dp[0][1][l][r] += dp[1][2][l + 1][r - 1];
                dp[0][1][l][r] += dp[2][2][l + 1][r - 1];

                dp[0][2][l][r] += dp[0][0][l + 1][r - 1];
                dp[0][2][l][r] += dp[1][0][l + 1][r - 1];
                dp[0][2][l][r] += dp[2][0][l + 1][r - 1];
                dp[0][2][l][r] += dp[0][1][l + 1][r - 1];
                dp[0][2][l][r] += dp[1][1][l + 1][r - 1];
                dp[0][2][l][r] += dp[2][1][l + 1][r - 1];

                dp[1][0][l][r] %= INF;
                dp[2][0][l][r] %= INF;
                dp[0][1][l][r] %= INF;
                dp[0][2][l][r] %= INF;
            } else {
                int k = s[l];

                for (int a = 0; a < 3; a++) {
                    for (int b = 0; b < 3; b++) {
                        for (int y1 = 0; y1 < 3; y1++) {
                            for (int x2 = 0; x2 < 3; x2++) {
                                if ((y1 != x2) || (y1 == 0 && x2 == 0)) {
                                    dp[a][b][l][r] += (dp[a][y1][l][k] * dp[x2][b][k + 1][r]) % INF;
                                    dp[a][b][l][r] %= INF;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    long long ans = 0;

    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            ans += dp[i][j][1][n];
            ans %= INF;
        }
    }
    printf("%lld", ans);

    return 0;
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值