NOIP模拟题 括号序列

题目描述
课堂上,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”(不可变
换) 。 (不含引号)

样例
3
()))
)))(
()
possible
impossible
possible

数据范围
对于 50%的数据,T<=5, N<=20;
对于 100%的数据,T<=10, N<=5000.

50分:暴力枚举改变的区间,然后再O(n)判断。
100分:dp,如果是左括号,a[i]=1 , 右括号 , a[i]=-1
对于 100% 的数据,需要使用动态规划解决。注意到一次变换操作
把序列划分成三段,前段与后段都没有修改。
所以我们可以使用动态规划:设 F[i][j][k] 为决定到前 i 位,目前的前
缀和为 j,并且现在第 i 位处于第 k 段(0 代表前,1 代表中,2 代
表后) ,是否可能。
DP 的转移方程为新增一位 i+1;i+1 位可以仍然与第 i 位位于同一段,
也可以位于不同的一段。最后的结果就是看 F[N][0] 是否可能。
时间复杂度:O(n^2*3)

50分

#include
#include
#include
#include
#define LL long long
#define I "impossible\n"
#define P "possible\n"
using namespace std;
int n,s[5009],sl[5009],sr[5009],len;
char c[5009];
bool check()
{
    for(int i=1;i<=len;i++)
    {
        sl[i]=sl[i-1]+s[i];
        sr[i]=sr[i-1]+!s[i];
        if(sr[i]>sl[i]) return 0;
    }
    return sl[len]==sr[len];
}
int main()
{
    freopen("brackets.in","r",stdin);
    freopen("brackets.out","w",stdout);
    scanf("%d\n",&n);
    while(n--)
    {
        int flag=0;
        gets(c+1);
        len=strlen(c+1);
        for(int i=1;i<=len;i++) s[i]=(c[i]=='(');
        for(int i=1;i<=len;i++)
        {
            for(int j=i;j<=len;j++)
            {
                for(int k=i;k<=j;k++) s[k]^=1;

                if(check()) 
                {
                    printf(P);
                    flag=1;
                    break;
                }
                for(int k=i;k<=j;k++) s[k]^=1;
            }
            if(flag) break;
        }
        if(!flag) printf(I);
    }
    return 0;
}

100分

#include
#include
#include
#include
#define LL long long
#define I "impossible\n"
#define P "possible\n"
using namespace std;
int n,T;
char s[5009];
int f[2][5009][3],a[5009];
void solve()
{
    int now=1;
    f[0][0][0]=1;
    for(int i=1;i<=n;i++)
    {
        memset(f[now],0,sizeof(f[now]));

        for(int j=0;j<=i-1&&j<=n-(i-1);j++)
        {
            if(f[now^1][j][0])//第一段 
            {
                //选择不翻
                if(a[i]<0&&j>0) f[now][j-1][0]=1;//接右括号 
                else if(a[i]>0) f[now][j+1][0]=1;//接左括号 

                //选择翻 
                if(a[i]>0&&j>0) f[now][j-1][1]=1;//将左括号翻为右括号 
                else if(a[i]<0) f[now][j+1][1]=1;//将右括号翻为左括号 
            }

            if(f[now^1][j][1])
            {
                if(a[i]>0&&j>0) f[now][j-1][1]=1;
                else if(a[i]<0) f[now][j+1][1]=1;//继续翻 

                if(a[i]<0&&j>0) f[now][j-1][2]=1;
                else if(a[i]>0) f[now][j+1][2]=1;//结束翻 
            } 

            if(f[now^1][j][2])
            {
                if(a[i]<0&&j>0) f[now][j-1][2]=1;//往后 
                else if(a[i]>0) f[now][j+1][2]=1;
            } 
        }
        now^=1;
    }
    now^=1;
    for(int i=0;i<3;i++)  
     if(f[now][0][i]){
         printf(P);
         return;
    } 
    printf(I);
}
int main()
{
    freopen("brackets.in","r",stdin);
    freopen("brackets.out","w",stdout);
    scanf("%d\n",&T);
    while(T--)
    {
        memset(f,0,sizeof(f));
        gets(s+1);
        n=strlen(s+1);
        for(int i=1;i<=n;i++)
        {
            if(s[i]=='(') a[i]=1;
            else a[i]=-1;
        }
        solve();
    }
    return 0;
}

你可能感兴趣的:(DP,优化,分析)