【区间DP】 Coloring Brackets CodeForces - 149D

题目来源

点我进入提交题目

反思

这个题目只对了5个数据点,主要的原因在与题目没有理解透彻,不能用一般的区间DP思想去解决这个题目。
以后做题目不能只注意题目表面的意义,还要多提炼题目内层含义!

以后还是读英文原题,多提高自己的阅读能力,对题目的理解也会更深刻一些。

题解

  • 提取题目限制条件

① 每个字符有三种情况:不染色,染成红色,染成蓝色
② 每对匹配的括号,有且仅有一个字符被染色
③ 所有相邻的两个字符,不能染成同一种颜色
每对字符串是固定的并且是合法的(我错在这个地方,直接哭死😭)

  • 题目类型分析

题目要求求方案数,那么首先就要想到DP,DP的三大判断嘛,最大最小值以及方案数。
括号问题,涉及到区间DP,而且有颜色状态。
那么初步分析之后,尝试使用区间DP + 状态机的思想方式去做这个题目。

  • DP分析

因为猜测考点为区间+状态机,所以状态集合设置为四维: f ( l , r , a , , b ) f(l, r, a, ,b) f(l,r,a,,b)
表示为:在区间 ( l , r ) (l ,r) (l,r)中, l l l点对应颜色状态为 a a a, r r r点对应颜色状态为 b b b的所有方案数。
a , b ∈ [ 0 , 2 ] a, b \in [0, 2] a,b[0,2] 0代表无颜色,1代表红,2代表蓝。

状态划分:
可分为两种情况:①l点对应的右括号在r点 ② l点对应的右括号不在r点,在k点。

对于第一种情况:状态转移对应内外组合,也就是他的状态转移是内部+外部的。
状态转移方程为: f [ l ] [ r ] [ a ] [ b ] = m o d ( f [ l ] [ r ] [ a ] [ b ] + f [ l + 1 ] [ r − 1 ] [ c ] [ d ] ) f[l][r][a][b] = mod(f[l][r][a][b] + f[l + 1][r - 1][c][d]) f[l][r][a][b]=mod(f[l][r][a][b]+f[l+1][r1][c][d])
这里我们需要注意的是 ( a , b ) (a, b) (a,b)对应的括号,染色的时候有且仅有一种颜色,而内部的 ( c , d ) (c, d) (c,d)对应的括号就可以随意一点,因为无论哪一种情况都可以得到(第二种情况都包含了),需要保证的是 a , c a, c a,c 或者 d , b d, b d,b不能染上相同的颜色。

对于第二种情况:状态转移对应左右组合。状态转移是左区间+右区间。
状态转移方程为: f [ l ] [ r ] [ a ] [ d ] = m o d ( f [ l ] [ r ] [ a ] [ d ] + m o d ( f [ l ] [ k ] [ a ] [ b ] ∗ f [ k + 1 ] [ r ] [ c ] [ d ] ) ) f[l][r][a][d] = mod(f[l][r][a][d] + mod(f[l][k][a][b] * f[k + 1][r][c][d])) f[l][r][a][d]=mod(f[l][r][a][d]+mod(f[l][k][a][b]f[k+1][r][c][d]))
同理:我们需要注意 ( a , b ) (a, b) (a,b)对应的括号,染色的时候有且仅有一种颜色,而内部的 ( c , d ) (c, d) (c,d)对应的括号颜色任意,需要保证的是 b , c b, c b,c不能染上相同的颜色。

这两个地方需要注意的细节我都踩坑了😓,不知道自己错哪里的朋友要注意以上这两种情况的细节哦!

因为括号是一一对应的,直接通过枚举的形式感觉比较麻烦,所以使用递归来解决这道题目。

  • 边界情况

当区间只有两个数的时候,符合条件的情况设置为1。

AC代码

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rep(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl
#define mod(x) (x) % MOD
#define ENDL "\n"
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 700 + 7, MOD = 1e9 + 7, INF = 0x3f3f3f3f;
char s[N];
int R[N], n;
ll f[N][N][3][3], ans;
stack<int> stk;

inline bool check(int a, int b){
    return (a == b) || (a && b);
}

inline bool judge(int b, int c){
    return b == c && b > 0;
}

void dp(int l, int r){
    if (l + 1 == r){
        _for (a, 0, 2) _for(b, 0, 2) {
            if (check(a, b)) continue;
            f[l][r][a][b] = 1;
        }
        return;
    }

    int k = R[l];
    if (k == r){
        dp(l + 1, r - 1);
        _for (a, 0, 2) _for(b, 0, 2) {
            if (check(a, b)) continue;
            _for (c, 0, 2) _for (d, 0, 2) {
                if (judge(a, c) || judge(d, b)) continue;
                f[l][r][a][b] = mod(f[l][r][a][b] + f[l + 1][r - 1][c][d]);
            }
        }
    }
    else{
        dp(l, k), dp(k + 1, r);
        _for (a, 0, 2) _for(b, 0, 2){
            if (check(a, b)) continue;
            _for(c, 0, 2) _for(d, 0, 2){
                if (judge(b, c)) continue;
                f[l][r][a][d] = mod(f[l][r][a][d] + mod(f[l][k][a][b] * f[k + 1][r][c][d]));
            }
        } 
    }
}

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cout.tie(0), cin.tie(0);

    cin >> s + 1;
    n = strlen(s + 1);
    _for (i, 1, n) if (s[i] == '(')  stk.push(i);
    else {
        R[stk.top()] = i;
        stk.pop();
    }

    dp(1, n);
    _for(a, 0, 2) _for(b, 0, 2) ans = mod(ans + f[1][n][a][b]);
    cout << ans << ENDL;
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值