贪心,括号序列(Rikka with Parenthesis II,HDU 5831)

来源:互联网 发布:dota2天梯各分段 知乎 编辑:程序博客网 时间:2024/05/27 02:30

判断一个括号序列是否合法,可以用栈来模拟。

就是左括号入栈,如果遇到右括号,但栈空或者不匹配那就错了。

如果最后栈不空也错了。

本题只有一种括号,可以直接O(n)扫一遍判断。

一开始cnt=0

左括号cnt++

右括号cnt--

如果cnt为负,那就错了。

如果最后cnt不为0,那也错了。


或者说,首先左右括号的个数要相等。

而且对任意的下标k,其左边的左括号数一定要大于等于右括号数。


然后剩下的问题就是贪心,不合法的位置一定是右括号的位置,我们要把它和一个左括号调换位置才行。

显然往前面换是不行的,因为对于现在的下标k,其左边的左括号数已经小于右括号数了。

如果往前面换,那么这个事实不会改变,即会使前面本来合法的序列变得不合法。

所以只能往后面换。

我们只能换一次,所以要尽量换好。

左括号是加,右括号是减,小于0就不行了。

所以左括号是越左越好,右括号是越右越好。

因此我们要和最右边的左括号交换位置。

最后还要判断下cnt是否等于0。


题目说一定要换一次,下标不同。

所以这里要特殊考虑一下。

如果本来就是合法的序列,我们交换两个左括号或两个右括号的位置,并不会改变这个序列。

否则就是n<=1或者()。

都不行。

n是正数,即n>=1,所以数据没有空串。

具体就是统计一下左右括号各有多少个,看看是否大于等于2。


一开始没有判断最后cnt是否等于0,错了一发。


代码

#include<stdio.h>#include<algorithm>using namespace std;const int maxn = 100010;int n;char s[maxn];bool ok(int& k){    k=0;    int cnt=0;    while(k<n)    {        if(s[k]=='(') cnt++;        else cnt--;        if(cnt<0) return false;        k++;    }    return true;}bool OK(){    int c1=0,c2=0;    for(int i=0;i<n;i++)    {        if(s[i]=='(') c1++;        else c2++;        if(c1>=2) return true;        if(c2>=2) return true;    }    return false;}bool oook(){    int c1=0,c2=0;    for(int i=0;i<n;i++)    {        if(s[i]=='(') c1++;        else c2++;    }    return c1==c2;}void solve(){    scanf("%d",&n);    scanf("%s",s);    if(!oook())    {        puts("No");        return;    }    int k;    if(!ok(k))    {        int k2=n-1;        while(k2>k&&s[k2]!='(') k2--;        if(k2<=k)        {            puts("No");            return;        }        swap(s[k],s[k2]);        if(!ok(k)) puts("No");        else puts("Yes");    }    else if(OK()) puts("Yes");    else puts("No");}int main(){    int T;    scanf("%d",&T);    while(T--) solve();    return 0;}