HDU 6299 Balanced Sequence(思维+排序)

链接

Balanced Sequence

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3282    Accepted Submission(s): 846


 

Problem Description

Chiaki has n strings s1,s2,…,sn consisting of '(' and ')'. A string of this type is said to be balanced:

+ if it is the empty string
+ if A and B are balanced, AB is balanced,
+ if A is balanced, (A) is balanced.

Chiaki can reorder the strings and then concatenate them get a new string t. Let f(t) be the length of the longest balanced subsequence (not necessary continuous) of t. Chiaki would like to know the maximum value of f(t) for all possible t.

 

 

Input

There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
The first line contains an integer n (1≤n≤105) -- the number of strings.
Each of the next n lines contains a string si (1≤|si|≤105) consisting of `(' and `)'.
It is guaranteed that the sum of all |si| does not exceeds 5×106.

 

 

Output

For each test case, output an integer denoting the answer.

 

 

Sample Input

 

2

1

)()(()(

2

)

)(

 

 

Sample Output

 

4

2

 

 

Source

2018 Multi-University Training Contest 1

 

题意:

给你n个串,让你给他们排序,使得总的平衡长度最大。平衡长度即一个串中平衡子串的长度的和。

例如)()(()( 中平衡长度为4'()'、'()',‘(())’该平衡长度为4、"(()())"平衡长度为6

总结来说A是平衡串,(A)也是平衡串,B是平衡串,则AB也是平衡串

 

解析:

这里关键就是这些串的排序。排序是让')'少'('多的串放在左边、 ')'多'('少的串放在右边。

在左边的串中[即都是')'少'('多的串]按照')'升序;在右边的串中[即都是')'多'('少的串]按照'('降序

 

下面是我想的简单的证明为什么这么排序?

 

在将字符串都转换成")))(((("的形式后,接下来的排序就要使值尽可能大
首先对于串"))((((((("和")))))))(("只要两个串相对位置是一左一右,就可以使值尽可能大
并且无论中间插入几个串,效果都是一样的。
因为"))(((((((" + "))))(((" + ")))))))((" 。
①串'('都用来匹配右边串的所有')'
并且每一个串的'('只能匹配该串右边的')'。换句话来说就是
③的')'可以供所有他左边的串匹配
所以我们就要让')'多的串尽可能放右边,同理'('多的串尽可能放左边。
这里怎么判断多还是少的话就是用一个串')'相对于'('的数量。(反之亦然)
因为两个串 第一个a个')'b个'('[a<b];第二个c个')'d个'(' [c>d]。这个我们可以很容易就看出应该第一个放在第二个左边
才能使值最大(min(b,c)>min(a,d))

接下来再是对于都是')'少'('多的情况
这种情况都是放在左边的位置,对于右边')'>'('的串的')',他们都可以用自己'('取匹配。
所以我们要讨论一下他们左边自己内部'('匹配')'的情况。
我这里推测的主要原因是——左边内部的匹配,a串的'('匹配其右边(这里称为b)串的')'一旦,a串的'('不够b的')'
,那么b剩余的')'就会自动移入最左边,永远也无法被匹配了。例如"))(((("和")))))((((((((",a串的'('不够b的')'
那么匹配完后就变成")))((((((((",b中剩余的一个')'永远无法被匹配。这个问题的主要原因是b左边的串的'('不够或者说
b的')'能匹配的'('太少了。解决这个问题我们起始就是让左边内部内(')'少'('多的情况)按照')'升序排列。即让')'多的串
尽可能在左边靠右的位置,使改串的')'能匹配更多的'(',减少')'的流失。
所以升序主要是使让左边的')'尽可能被匹配,减少他的流失,因为一旦左边')'无法被匹配,就会自动移入最左边而流失。
右边的情况也是一样的,在其内部来说,按照'('降序排列,使'('的流失减少,能尽可能被匹配。

排序完后,只需要从左边扫到最右边就可以了。

注意等于的情况也不要忘了考虑,等于必须放在中间位置。
因为两个串 第一个a个')'b个'('[a<b];第二个c个')'d个'(' [c=d]。那么②串一定放在①右边
同理①[a>b]②[c=d],②放在①左边。所以就放在左边的右边的左边和左边的右边那么就放中间

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 1e5+100;
typedef struct node
{
    int x;
    int y;
}node;
char str[MAXN];
node f[MAXN];

char ms[MAXN];
int top;
int ans;
void solve(int x)
{
    int len=strlen(str);
    int a,b;
    top=a=b=0;
    for(int i=0;i<len;i++)
    {
        if(str[i]=='(')
        {
            ms[top++]='(';
            b++;
        }
        else
        {
            if(ms[top-1]=='(')
            {
                top--;
                b--;
                ans+=2;
            }
            else
            {
                ms[top++]=')';
                a++;
            }
        }
    }
    f[x].x=a;
    f[x].y=b;
}

bool cmp1(node a,node b)
{
   if(a.x>=a.y&&b.x<b.y)   //')'多'('少  &&  ')'少'('多
   {
       return false;
   }
   else if(a.x<a.y&&b.x>=b.y)   //')'少'('多  &&  ')'多'('少
   {
       return  true;
   }
   else if(a.x<a.y&&b.x<b.y)   //')'少'('多  &&  ')'少'('多
   {
       return a.x<b.x;
   }
   else return a.y>b.y;  //')'多'('少  &&  ')'多'('少
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        ans=0;
        scanf("%d",&n);

        for(int i=1;i<=n;i++)
        {
            scanf("%s",str);
            solve(i);
        }
        sort(f+1,f+1+n,cmp1);
        int now=f[1].y;
        for(int i=2;i<=n;i++)
        {
            ans+=min(now,f[i].x)*2;
            if(now>f[i].x) now-=f[i].x;
            else now=0;
            now+=f[i].y;
        }
        printf("%d\n",ans);


    }
    return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值