CodeForces 149 D. Coloring Brackets

题目来源:click
一开始就把题目读错了…,以为给出的括号不是一定都是匹配的,想到组合数学那方面去过于复杂。(认真读题目)
题意给出的是一个完全匹配的字符串。
染色要求是相匹配的括号一定有一个要染色而且只有一个;相邻的括号不能染相同的颜色;每个括号只有三种情况,不上色,上红色,上蓝色。
首先我们应该要确定谁和谁相匹配,用一个数组ma[]来记录下标。
之前有过一个括号匹配的区间dp问题dp[i][j]表示从i-j区间有多少个匹配的括号。
这道题目也可以如此思考成区间dp,但是题目中增加了染色的问题,所以可以转化为一个四维的dp去写,dp[i][j][k1][k2]表示是区间的起点i染成k1,j染成k2的方法数,可从小到大递推出答案。题解非常巧妙的加上了dfs。
分为了三种情况去深搜:
1.当l==r-1的时候毫无疑问四种情况给上数值即可。
2.当ma[l]==r的时候,两端的括号相匹配:
分为染色情况匹配的括号只染色一个而且必须染一个。
即dp[l][r][][]+=dp[l+1][r+1][][],详情见代码。
3.两端的括号不匹配的话,找出中间的匹配值先进行dfs找出最优解,
之后相乘即可得到l-r区间的方法数,因为l-k,k+1-r互不影响但需要考虑k与k+1不能染相同的颜色。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<map>
#include<algorithm>
#include<queue>
#define MAX_len 50100*4
using namespace std;
typedef long long  ll;
const ll mod=1e9+7;
char s[710];
int ma[710];
ll dp[710][710][3][3];
void solve(int l, int r)
{
    if(r<l)
        return ;
    int i,j,k;
    if(l==r-1)
    {
        dp[l][r][0][1]=1;
        dp[l][r][0][2]=1;
        dp[l][r][2][0]=1;
        dp[l][r][1][0]=1;
        return ;
    }
    if(ma[l]==r)
    {
        solve(l+1,r-1);
        for(i=0;i<3;i++)
        {
            for(j=0;j<3;j++)
            {
                if(j!=1)
                dp[l][r][0][1]=(dp[l][r][0][1]+dp[l+1][r-1][i][j])%mod;
                if(j!=2)
                dp[l][r][0][2]=(dp[l][r][0][2]+dp[l+1][r-1][i][j])%mod;
                if(i!=1)
                dp[l][r][1][0]=(dp[l][r][1][0]+dp[l+1][r-1][i][j])%mod;
                if(i!=2)
                dp[l][r][2][0]=(dp[l][r][2][0]+dp[l+1][r-1][i][j])%mod;
            }
        }
        return ;
    }
    int tmp=ma[l];
    solve(l,tmp);
    solve(tmp+1,r);
    for(i=0;i<3;i++)//l
    for(j=0;j<3;j++)//r
    for(int k1=0;k1<3;k1++)//tmp
    for(int k2=0;k2<3;k2++)//tmp+1
    {
        if((k1==1||k2==2)&&(k1==k2))
            continue;
        if((i==0&&(k1==1||k1==2))||(i==1&&(k1==0))||(i==2&&k1==0))
        dp[l][r][i][j]=(dp[l][r][i][j]+(dp[l][tmp][i][k1]*dp[tmp+1][r][k2][j])%mod)%mod;
    }
    return ;
}
int main()
{
    scanf("%s",s);
    int i,j,n;
    n=strlen(s);
    stack<int>temp;
    int len=0;
    for(i=0;i<n;i++)
    {
        if(s[i]=='(')
        {
            temp.push(i);
            continue;
        }
        else{
            if(!temp.empty())
            {
                ma[i]=temp.top();
                ma[temp.top()]=i;
                temp.pop();
            }
        }
    }
    memset(dp,0,sizeof(dp));
    solve(0,n-1);
    ll ans=0;
    for(i=0;i<3;i++)
    {
        for(j=0;j<3;j++)
        {
            ans+=dp[0][n-1][i][j];
            ans%=mod;
        }
    }
    printf("%I64d",ans);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值