括号序列 解题报告

括号序列

题目背景

你不努力,未来别人腿咚的墙还是你砌的!

题目描述

课堂上,Felix刚刚学习了关于括号序列的知识。括号序列是一个只由左括号"("和右括号")"构成的序列:进一步的,一个合法的括号序列是指左括号和右括号能够一一匹配的序列。

如果用规范的语言说明,一个合法的括号序列可以有以下三种形式:

1 s=""(空串),s是一个合法的括号序列

2 s=XY,其中X,Y均为合法的括号序列,则S也是一个合法的括号序列;

3 s=(X),其中X为合法的括号序列,则S也是一个合法的括号序列。

这时老师在黑板上写出了一个括号序列:"()))()"

Felix一眼就看出这个序列并不是合法的括号序列。

这时老师提出了一个这样的问题:能够在序列中找出连续的一段,把这一段里面的左括号变成右括号,右括号变成左括号,变换之后整个序列可以变成合法的呢?

Felix想到,可以把[3..5]进行调换,这样序列就会变为()(()),是一个合法的序列。很明显,不止有一种方法可以使整个序列合法。

这时,老师又在黑板上写出了一个长度为N的括号序列。Felix想,能否对这个序列进行至多一次变换,使他变合法呢?

输入输出格式

输入格式:

第一行一个整数T,代表数据的组数;接下来T行,每一行一组数据。

每组数据一行,代表给出的括号序列。

输出格式:

输出共T行,对于每组数据,输出“possible”(可以变换)或者“impossible”(不可变换)。(不含引号)

说明

对于50%的数据,T<=5,N<=20;

对于100%的数据,T<=10,N<=5000。


一开始想把已有的匹配括号全部缩掉直接贪心,搞了一会儿发现是错的。

无奈,打了个暴力,枚举翻转区间并检查是否合法,居然还错了。。

缺乏想到正解的原因是,我只知道拿栈来判断括号匹配,殊不知有实用性更好的方法。

如果把括号抽象为一个数列,左括号为1,右括号为-1。

对S的某个前缀p,如果数列的前缀和\(f[p]\)的值大于0,则说明左括号数量不比右括号少。

对于这个前缀p,如果每个\(f\)数组值大于等于0且\(f[p]=0\),则这个前缀p是匹配的。

由此引入这个题的正解dp

\(dp[i][j][k]\)表示前缀\(i\)前缀和为\(j\)时处于\(k\)状态(0为翻转区间左边,1为翻转区间,2为翻转区间右边),进行可达性统计

初始:\(dp[0][0][0]=1\)
目标:\(dp[n][0][0/1/2]\)

可以滚动一下优化空间,另外这个题居然有点卡常。。


#include <cstdio>
#include <cstring>
int max(int x,int y){return x>y?x:y;}
const int N=5010;
int f[N],t,dp[2][N][3];
char c[N];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",c);
        memset(f,0,sizeof(f));
        memset(dp,0,sizeof(dp));
        int n=strlen(c);
        for(int i=0;i<n;i++)
            f[i+1]=c[i]=='('?1:-1;
        dp[0][0][0]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=i;j++)
                for(int k=0;k<=2;k++)
                    dp[i&1][j][k]=0;
            int t=f[i];
            for(int j=0;j<=i;j++)
            {
                if(j>=t)
                {
                    dp[i&1][j][0]=dp[i-1&1][j-t][0];
                    dp[i&1][j][2]=max(dp[i-1&1][j-t][1],dp[i-1&1][j-t][2]);
                }
            }
            t=-t;
            for(int j=0;j<=i;j++)
                if(j>=t)
                    dp[i&1][j][1]=max(dp[i-1&1][j-t][0],dp[i-1&1][j-t][1]);
        }
        if(dp[n&1][0][0]||dp[n&1][0][1]||dp[n&1][0][2])
            printf("possible\n");
        else
            printf("impossible\n");
    }
    return 0;
}

2018.6.21

猜你喜欢

转载自www.cnblogs.com/ppprseter/p/9210530.html