[CodeForces-149D] 题解

一道难度适中的区间dp,用记忆化搜索比较好处理。
将字符串涂色的状态定义成动态规划的状态,容易推出需要记录区间的左右端点(是个区间dp基本都得这样做),由于题目限制左右端点的涂色,所以要记录左右端点信息,才知道下一步如何递推。
所以,得出结论:
用dp[a][b][c][d] 四位数组,分别表示字符串左区间、字符串右区间、左端点颜色、右端点颜色

之后就是转移过程,见代码详尽注释。

#include <iostream>
#include <string.h>
#include <stack>
#include <algorithm>
#include <string>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
int n;
string s;
stack <int> st;
int m[720];
ll dp[720][720][3][3];

ll dfs(int a, int b, int c, int d)
{
    //a表示左端点位置 b表示右端点位置 c表示左端点颜色 d表示右端点颜色
    //0表示没涂色 1表示涂了一种颜色 2表示涂了另一种颜色
    ll res = 0;
    if (b <= a || dp[a][b][c][d] != -1)//如果区间已经被分的不能再分,或者上次已经算过,返回
    {
        if (b <= a)//只有一种方案
            return 1;
        else
            return dp[a][b][c][d];
    }
    if (b == m[a])//遇到这个区间左右端点恰好匹配 那么左右端点的方案已经固定了 向内部推一层
    {
        if (c != 1) res += dfs(a + 1, b - 1, 1, 0) % mod;
        //如果左端点颜色不为1,则内部相邻的左端点颜色可以为1,左端点涂了色,右端点一定不能涂色
        if (d != 1) res += dfs(a + 1, b - 1, 0, 1) % mod;
        //如果右端点颜色不为1,则同理
        if (c != 2) res += dfs(a + 1, b - 1, 2, 0) % mod;
        //如果左端点颜色不为2,则同理
        if (d != 2) res += dfs(a + 1, b - 1, 0, 2) % mod;
        //如果右端点颜色不为2,则同理
    }
    else
    {
        //这个区间左右端点不匹配,则有四种情况
        res += (dfs(a + 1, m[a] - 1, 0, 1) * dfs(m[a] + 1, b, 1, d)) % mod;
        res += (dfs(a + 1, m[a] - 1, 0, 2) * dfs(m[a] + 1, b, 2, d)) % mod;
        ll p = dfs(m[a] + 1, b, 0, d);
        if (c != 1) res += (p * dfs(a + 1, m[a] - 1, 1, 0)) % mod;
        if (c != 2) res += (p * dfs(a + 1, m[a] - 1, 2, 0)) % mod;
    }
    dp[a][b][c][d] = res % mod;//记录已经算好的
    return res % mod;
}
int main() {
    while (getline(cin, s)) 
    {
        n = s.size();
        for (int i = 0; i < n; i++)//用m数组记录匹配括号的信息
        {
            if (s[i] == '(') st.push(i);
            else
            {
                m[st.top()] = i;
                st.pop();
            }
        }
        memset(dp, -1, sizeof(dp));//进行记忆化搜索之前,将dp数组置为-1,避免有0的方案出现却未被计算
        printf("%lld\n", dfs(0, n - 1, 3, 3));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值