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;
}