传送门:HDU 6299
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
题意:
给你n个包含’(‘与’)’的字符串,可以将这些字符串任意排序,求所有排序中,子序列是符合正规括号序列的最大长度。
是n个字符串可以自由排序,不是各个字符串可以自由排序。
题解:
可以先将每个字符串中已经合法的括号去除,可以想象,去除后,应该只剩三种情况的字符串(也可以看成一种,另外两种的这一种的特殊情况):
- 只剩左括号,如:((( ;
- 剩左括号和右括号,右括号一定在左边,如:))( ;
- 只剩右括号,如:))) ;
那么我们只要去除合法括号后,用一个结构体存下每个字符串的左括号数目和右括号数目即可。
这时我们又可将这些字符串分成两个部分:
- 左括号数目>右括号数目
- 左括号数目<=右括号数目
先记左括号数目>右括号数目为正贡献,反之则为负贡献。
我们很自然的就可以想到右括号少的应该放在最前面,因为这些括号一定是浪费了的,当然越少越好。左括号少的应该放在最后面,因为最后的左括号也一定的浪费的。
那么我们就可以得出
1. 当贡献为正时,我们应该以右括号数目由小到大排序
2. 当贡献为负时,我们应该以左括号数目由大到小排序
对于排完序的字符串我们只要模拟走一遍即可。
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<stack>
#define ll long long
using namespace std;
const int N=110000;
int t,n;
char s[N];
struct Node
{
int right,left;
}str[N];
int change(char c[],int id)//去除合法括号,并记录长度
{
stack<char> ss;
int l=strlen(c);
int sum=0;
for(int i=0;i<l;i++)
{
if(c[i]=='(')
{
str[id].left++;
ss.push(c[i]);
}
else
{
if(!ss.empty())
{
ss.pop();
str[id].left--;
sum+=2;
}
else str[id].right++;
}
}
return sum;
}
//记贡献为左括号个数减右括号个数,贡献大于0的在左,小于0的在右
//贡献大于0的按右括号个数由小到大排序,贡献小于0的按左括号个数由大到下排序
bool cmp(Node a,Node b)
{
if(a.right<=a.left&&b.right>b.left) return true;
else if(a.right>a.left&&b.right<=b.left) return false;
else if(a.right<=a.left&&b.right<=b.left) return a.right<b.right;
else return a.left>b.left;
}
int main()
{
scanf("%d",&t);
while(t--)
{
memset(str,0,sizeof(str));
int ans=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%s",s);
ans+=change(s,i);
}
if(n==1) printf("%d\n",ans);
else
{
sort(str,str+n,cmp);
int num=0;//记录左括号的数目
for(int i=1;i<n;i++)
{
num+=str[i-1].left;
if(num>str[i].right)//左括号的数目大于右括号数目
{
num-=str[i].right;//取掉右括号的数目
ans+=2*str[i].right;//长度增加
}
else//小于
{
ans+=2*num;
num=0;//左括号全部被匹配
}
}
printf("%d\n",ans);
}
}
return 0;
}