poj 1141 题解

题意简述

给定一个括号序列(长度 &lt; = 100 &lt;=100 <=100),但是这个括号序列不一定匹配,请用最少的添加配好这个序列。输出配好的序列。

数据

输入
( [ ( ] ([(] ([(]
输出
( ) [ ( ) ] ()[()] ()[()]

思路

这个。。。第一眼看到,栈?
仿佛不能做。。。但是这个题好像珂以区间 D P DP DP
d p [ l ] [ r ] dp[l][r] dp[l][r]表示从 l l l r r r的最优解, p o s [ l ] [ r ] pos[l][r] pos[l][r]表示 l l l r r r取最优解时的断点(如果 = − 1 =-1 =1就表示不需要断点)。
显然有边界 d p [ i ] [ i ] = 1 dp[i][i]=1 dp[i][i]=1,因为长度为 1 1 1的字符串无论如何都需要补一个使得能匹配。
考虑转移。
如果 s [ l ] s[l] s[l] s [ r ] s[r] s[r]能匹配,显然有 d p [ l ] [ r ] = d p [ l + 1 ] [ r − 1 ] dp[l][r]=dp[l+1][r-1] dp[l][r]=dp[l+1][r1],断点什么的就交给中间(即 l + 1 l+1 l+1 r − 1 r-1 r1)记录去吧,反正 l l l r r r是不用记录的,直接 p o s = − 1 pos=-1 pos=1处理。
如果不能匹配,就要考虑别的情况了。显然, d p [ l ] [ r ] = m i n { d p [ l ] [ k ] + d p [ k + 1 ] [ j ] } dp[l][r]=min\{dp[l][k]+dp[k+1][j]\} dp[l][r]=min{dp[l][k]+dp[k+1][j]},其中 l &lt; = k &lt; r l&lt;=k&lt;r l<=k<r。一边枚举 k k k,一遍记录最优断点,存到 p o s pos pos里面。

现在考虑如何输出解。
和 2003NOIP-TG加分二叉树 那个题类似,我们定义 D F S ( l , r ) DFS(l,r) DFS(l,r)为输出 l l l r r r的解的函数。
如果 l &gt; r l&gt;r l>r,直接返回(不合FA♂)
如果 l = = r l==r l==r,那么一定要补上一个的。如果 s [ l ] = = ( 或 ) s[l]==(或) s[l]==(),那么输出 ( ) () ()。否则输出 [ ] [] []
如果 p o s [ l ] [ r ] = − 1 pos[l][r]=-1 pos[l][r]=1,记得是什么情况么?我们只要 D F S ( l + 1 , r − 1 ) DFS(l+1,r-1) DFS(l+1,r1),然后两边套上括号即可。当然,套那种括号就不用判了,根据 p o s [ l ] [ r ] = − 1 pos[l][r]=-1 pos[l][r]=1的神奇性质,我们只要在两边套上 s [ l ] s[l] s[l] s [ r ] s[r] s[r]就是那一对括号。
否则,十分简单,分两块即可。 D F S ( l , p o s [ l ] [ r ] ) DFS(l,pos[l][r]) DFS(l,pos[l][r]),然后 D F S ( p o s [ l ] [ r ] + 1 , r ) DFS(pos[l][r]+1,r) DFS(pos[l][r]+1,r)即可。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 110
using namespace std;

namespace Flandle_Scarlet
{
    char s[N];int n;

    int dp[N][N];
    int pos[N][N];
    bool check(char x,char y)//判断x和y是否是匹配的括号
    {
        if (x=='(' and y==')') return 1;
        if (x=='[' and y==']') return 1;
        return 0;
    }
    void DP()
    {
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;++i)
        {
            dp[i][i]=1;//边界条件
        }

        for(int len=1;len<n;++len)//枚举长度
        {
            for(int l=0;l+len<n;++l)//枚举左端点
            {
                int r=l+len;//计算右端点
                dp[l][r]=0x7fffffff;//初始化很大

                if (check(s[l],s[r]))//如果s[l]和s[r]能匹配
                {
                    if (dp[l+1][r-1]<dp[l][r])//就要考虑用dp[l+1][r-1]更新dp[l][r]
                    {
                        dp[l][r]=dp[l+1][r-1];
                        pos[l][r]=-1;
                    }
                }

                for(int k=l;k<r;++k)//枚举断点k
                {
                    if (dp[l][r]>dp[l][k]+dp[k+1][r])
                    {
                        dp[l][r]=dp[l][k]+dp[k+1][r];
                        pos[l][r]=k;//更新答案
                    }
                }
            }
        }
    }
    void DFS(int l,int r)//都解释过,此处注释略
    {
        if (l>r) return;
        if (l==r)
        {
            if (s[l]=='(' or s[r]==')')
            {
                printf("()");
            }
            else
            {
                printf("[]");
            }
            return;
        }
        else if (pos[l][r]==-1)
        {
            putchar(s[l]);
            DFS(l+1,r-1);
            putchar(s[r]);
        }
        else
        {
            DFS(l,pos[l][r]);
            DFS(pos[l][r]+1,r);
        }
    }

    void Main()
    {
        while(gets(s)!=NULL)//听说不gets会炸(白费我10次提交)
        {
            n=strlen(s);
            DP();
            DFS(0,n-1);
            putchar('\n');//结构清晰
        }
    }
}
int main()
{
    Flandle_Scarlet::Main();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值