【动规】POJ 1141 Brackets Sequence

本文介绍了一种通过区间动态规划解决括号序列问题的方法,旨在寻找最短的有效括号序列,包含给定的括号子序列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

题目描述

Let us define a regular brackets sequence in the following way:

  1. Empty sequence is a regular sequence.
  2. If S is a regular sequence, then (S) and [S] are both regular sequences.
  3. If A and B are regular sequences, then AB is a regular sequence.

For example, all of the following sequences of characters are regular brackets sequences:

(), [], (()), ([]), ()[], ()[()]

And all of the following character sequences are not:

(, [, ), )(, ([)], ([(]

Some sequence of characters ‘(‘, ‘)’, ‘[‘, and ‘]’ is given. You are to find the shortest possible regular brackets sequence, that contains the given character sequence as a subsequence. Here, a string a1 a2 … an is called a subsequence of the string b1 b2 … bm, if there exist such indices 1 = i1 < i2 < … < in = m, that aj = bij for all 1 = j = n.

输入

The input file contains at most 100 brackets (characters ‘(‘, ‘)’, ‘[’ and ‘]’) that are situated on a single line without any other characters among them.

输出

Write to the output file a single line that contains some regular brackets sequence that has the minimal possible length and contains the given sequence as a subsequence.

样例输入

([(]

样例输出

()[()]

题目大意

输入一个由小括号或中括号组成的序列,用最少的操作使它变为正则括号表达式。
正则括号表达式:

  • 空串是正则括号表达式
  • 若S为正则括号表达式,那么(S)或[S]都是正则括号表达式

思路

最优方案计算

区间DP,设S[i]到S[j]添加f[i][j]个括号能变为正则括号表达式。
分两类:

  • S[i]与S[j]能匹配:
    f[i][j]=max(f[i][j],f[i+1][j1])
  • S[i]与S[j]不能匹配:
    f[i][j]=max(f[i][k]+f[k+1][j]|ik<j)

对于第一种,S[i]与S[j]能匹配,自然可以不管它;
对于第二种,不能匹配时,就需要在之间找到一个断点k,分别处理i~k和k+1~j这一段,使它们变成正则括号表达式,那么S[i]和S[j]匹不匹配就无妨了

注意上面加粗的几个字,当你根据我的分类写出下面的语句时,就WA了,好开心~

if(match(S[i],S[j])) /*...*/
else /*...*/

如果这样,显然可以举出一个反例:
[][]
程序会先判断S[1]和S[4],他们匹配,恩很好,所以f[i][j]=f[i+1][j-1],然后会发现f[i+1][j-1]是2(因为S[2]到S[3]需要加一个’[‘和一个’]’),于是答案是2,正确答案是0。

所以,无论f[i]f[j]是否能匹配,都需要找一个点分段,执行一遍f[i][j]=max(f[i][k]+f[k+1][j]|ik<j),即把之前的else去掉即可。

显然这个可以用记忆化递归实现,很好想,但是速度不是很快。
用递推的方式,就要赋初值,f[i][i]=1不解释。

最优方案输出

至于输出方案很简单,但是我就不告诉你

f[i][j]的“断点”为pre[i][j]f[i][j]=f[i][pre[i][j]]+f[pre[i][j]+1][j]
在for循环找断点时即可得到pre[i][j]。
然后递归print,具体看程序。

代码

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

#define MAXN 100
char str[MAXN+5];
int Len,f[MAXN+5][MAXN+5],pre[MAXN+5][MAXN+5];

bool match(char x,char y)//括号x和y能否匹配
{
    return (x=='('&&y==')')||(x=='['&&y==']');
}

void print(int i,int j)//输出i~j区间方案
{
    if(i>j) return;//区间不存在
    if(i==j)//长度为1的区间,怎样添加括号是确定的
    {
        if(str[i]=='('||str[i]==')') printf("()");
        else printf("[]");
    }
    else if(pre[i][j])//说明之前分类的第二种更优
    {
        print(i,pre[i][j]);//分段
        print(pre[i][j]+1,j);
    }
    else//分类的第一种更优,即S[i]和S[j]在最终的答案中是需要匹配的
        //不是能匹配就一定要输出,例如[][],S[1]和S[4]能匹配,但是不是最优
    {
        printf("%c",str[i]);
        print(i+1,j-1);
        printf("%c",str[j]);
    }
}

int main()
{
    scanf("%s",str+1);
    Len=strlen(str+1);
    for(int i=1;i<=Len;i++)
        f[i][i]=1;//对于i~i,肯定要添加1个括号才能匹配
    for(int i=Len-1;i>=1;i--)
        for(int j=i+1;j<=Len;j++)//区间DP模型
        {
            f[i][j]=Len+1;//极大值,因为i~j最多可能放Len个括号
                          //让我很恶心的是之前赋Len就WA了= =
            if(match(str[i],str[j]))
                f[i][j]=min(f[i][j],f[i+1][j-1]);//能够匹配的情况
            for(int k=i;k<j;k++)//不论能否匹配都要找一次,因为有可能有更好的
                if(f[i][j]>f[i][k]+f[k+1][j])
                {
                    f[i][j]=f[i][k]+f[k+1][j];
                    pre[i][j]=k;//存断点
                }
        }
    //printf("%d\n",f[1][Len]);//输出最少添加的括号数量
    print(1,Len);puts("");
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值