链接:http://acm.hdu.edu.cn/showproblem.php?pid=6299
题意: 一般的括号序列求最长匹配子序列,这题给了多个括号序列让你把他们重排后要求得到最长匹配子序列最长。。。
题解:首先自己对括号序列就不是很熟悉orz
括号序列匹配就是,要求左右成对,然后成对的括号之间只能包含或者相离不能交叉
(()),()(), 就是合法的
意识到的一点是已经匹配完成的括号从序列中删除后不影响剩下的括号匹配
所以这题拿到一个串后先用栈把串处理下把所有的串内匹配的括号删除,
处理后,这个串只剩下不能匹配的,所以一定是 a个‘)’+b个'('的形式(a,b>=0)
之后考虑剩下的串怎么拼在一起形成更多匹配的括号对
首先 最前头的肯定是放a=0的串 (((((((((((
最后头放b=0的串 ))))))))))
然后考虑中间的串怎么排序 ))))))))(((((((((((
贪心的话肯定'(' 更多的串放在靠左的位置,这样才能有更多的匹配
对于栈处理后的串可以用变量 p[i]表示,p[i].l ,p[i].r 分别表示串里向左,向右括号的数目
所以l<=r的串放左边,l>=r的串放右边
然后l<=r的串之间,按照 ‘)’ 越少的放左边, l>=r的串之间,按照 '( '越少的放左边的方式排序
对于拍好序的串直接手工模拟下栈操作就OJBK了
#include<bits/stdc++.h>
using namespace std;
int t,n;
struct zz{
int l,r;
}p[100009];
stack<char> sta;
//v1,v3放只有一种括号的串,v2放两种括号都有的串
vector<zz>v1,v2,v3;
char st[100009];
int cmp(zz a,zz b)
{ if (a.r>=a.l&&b.r>=b.l) return a.l<b.l; //两个都是右向的括号更多
else if (a.r>=a.l)return 1; //a右向多
else if (b.r>=b.l)return 0;
else return a.r>b.r; //两个都是左向的多
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
v1.clear();v2.clear();v3.clear();
int ans=0;
for (int j=0;j<n;j++)
{ scanf("%s",st);
while(!sta.empty())sta.pop();
int len=strlen(st);
sta.push(st[0]);
for (int i=1;i<len;i++)
{ if (st[i]==')'&&!sta.empty()&&sta.top()=='(')
{ans++;sta.pop();
}else sta.push(st[i]);
}
int a=0,b=0;//a为向左括号,b为向右括号
while(!sta.empty())
{ if (sta.top()==')')a++;
else b++;
sta.pop();
}
p[j].l=a;p[j].r=b;
if (b&&a==0)v1.push_back(p[j]);
else if(a&&b==0)v3.push_back(p[j]);
else v2.push_back(p[j]);
}
sort(v2.begin(),v2.end(),cmp);
//step 存的是现在栈里存在的左括号的数目
int step=0;
for (int i=0;i<v1.size();i++)step+=v1[i].r;
for (int i=0;i<v2.size();i++){
ans+=min(step,v2[i].l);
step-=v2[i].l;if (step<0)step=0;
step+=v2[i].r;
}
for (int i=0;i<v3.size();i++){
if (step==0)break;
ans+=min(step,v3[i].l);
step-=v3[i].l;if (step<0)step=0;
}
printf("%d\n",ans*2);
}
return 0;
}