题目描述
课堂上,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;
}