poj 1141 题解

题意简述

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

数据

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

思路

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

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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值