HDU 6299 - Balanced Sequence [2018杭电多校联赛第一场 B](贪心)
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=6299
【题意】
对于只由’(‘和’)’构成的字符串,定义正规字符串:
1.空串是正规字符串
2.如果A和B是正规字符串,那么A+B也是正规字符串
3.如果A是正规字符串,那么(A)也是正规字符串
现在给n个括号序列,可以对它们任意排列后构成一个括号序列,然后要使得这个序列的一个子序列构成一个正规字符串,问你这个正规字符串的最大长度是多少?
【输入格式】
第一行为数据组数T,接下来每组数据第一行为n(n<=1e5)下面n行每行一个括号序列,每个序列的长度不超过1e5,所有序列长度之和不超过5e6
【输出格式】
每组数据输出一个整数,代表重排之后序列的子序列中最长的正规字符串长度
【思路】
首先题目定义的正规字符串其实就是左右括号相匹配的字符串,问题可以转换成将这n个序列重新排列之后,最多可以消掉多少个’(‘和’)’.我们可以先对每一个字符串用栈进行括号匹配,然后把能消掉的都消掉,消完剩下的字符串只有下面的三种情况
1 (((((…((((( 即若干左括号
2 )))))…))))) 即若干右括号
3 )))..)(…((( 即左边若干个右括号,右边若干个左括号
然后对这些字符串重排,可以肯定的是类型1的字符串全部放到左边,类型2的字符串全部放到右边,关键问题就是类型3的字符串内部怎么进行排列。
首先要把类型3的字符串再细分成两种,一种是类似))((((((((这样左少右多的,一种是类似))))))))((这样左多右少的,左少右多的一定放在左多右少的前面。
然后如果两个字符串全是左少右多的,那么根据左边的)数量从小到大排序
如果两个字符串全是左多右少的,那么根据右边的(数量从大到小排序
至于为什么是这样贪心,我也不会证明,就是在纸上画出来好多个例子慢慢试的…..
#include<bits/stdc++.h>
using namespace std;
const int maxn=5000050;
struct node{
int a,b;
node(int aa=0,int bb=0):a(aa),b(bb){}
bool operator<(const node& e)const{
if(b>=a && e.b<=e.a) return 0;//前短后长 的放 前长后短 的前面
if(b<=a && e.b>=e.a) return 1;
if(b>=a && e.b>=e.a){ //都是 前短后长,前面越小越靠前
return a>e.a;
}
if(b<=a && e.b<=e.a){ //都是 前长后短,后面越大越靠前
return b<e.b;
}
}
};
int n;
char s[maxn];
stack<char> st;
vector<node> vt;
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
int ans=0;
int L=0,R=0;//记最左端的'('个数和最右端的')'个数
vt.clear();
for(int i=0;i<n;++i){
scanf("%s",s);
int a=0,b=0;
while(st.size()) st.pop();
for(int j=0;s[j];++j){
if('('==s[j]){
++a;
st.push(s[j]);
}
else {
++b;
if(st.size()){
st.pop();
++ans;
--a;
--b;
}
}
}
if(b==0) L+=a;
else if(a==0) R+=b;
else vt.push_back(node(a,b));
}
sort(vt.begin(),vt.end());
int tmp=L;//记录当前可用的'('个数
for(int j=0;j<vt.size();++j){
ans+=min(tmp,vt[j].b);
if(tmp<=vt[j].b) tmp=0;
else tmp-=vt[j].b;
tmp+=vt[j].a;
}
ans+=min(tmp,R);
ans<<=1;
printf("%d\n",ans);
}
return 0;
}