【动规】POJ 1141 Brackets Sequence

来源:互联网 发布:日语软件测试工程师 编辑:程序博客网 时间:2024/06/02 20:40

题目

题目描述

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 100char 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("");}
原创粉丝点击