1. 程式人生 > >NOIP模擬題 括號序列

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<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#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<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#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;
}