Description
给定一个由左括号'('和右括号')'组成的字符串s,其中第i个括号的权值为ai。
我们定义一个括号序列t为合法括号序列
,当且仅当满足下列条件之一:
1.t为空串
2.t=(A),其中A为一个合法括号序列
3.t=AB,其中A,B均为合法括号序列
比如(()()),()(())均为合法括号序列,但((), ())(不是合法括号序列。
Input
输入第一行一个整数n,表示s的长度。
第二行一个长度为n,且由括号组成的字符串s。
第三行n个整数,第i个整数ai表示第i个括号的值。
Output
输出仅一行一个整数,表示答案。
Sample Input1
6
())(()
1 2 1 2 1 2
Sample Output1
7
Sample Input2
6
((()))
2 1 -2 1 2 3
Sample Output2
8
Hint
40%的数据n<=20,0<=ai<=100
接下来30%的数据 n<=2000,|ai|<=1e5
接下来30%的数据 n<=2e5,|ai|<=1e3,左括号全都在右括号的左边
题解
官方题解:
分情况讨论
n<=2000时 用dp,F [ i ] [ j ]表示前i个中有j个左括号时的最大值
n >2000时,分别对左括号的值和右括号的值排序(cmp从大到小),用for循环从1走一遍,若a[i]+b[i]>0 ans+=a[i]+b[i],否则return.
但是我的思路可以把n取到1e6并且不需要特殊性质啊!QAQ
我们先假设现在已经从头开始扫了一段括号序列,并且前一段已经标记了最优的序列有哪些括号。对于现在新加进来的括号,我们仍然要试图维持当前选择最优。
如果是左括号,我们不做任何处理(也做不了什么);
如果是右括号,那么不外乎三种情况:
1.不做任何处理;
2.在这个右括号之前找一个未被选的左括号,与其配对(标记此左括号和右括号);
3.在这个右括号之前找一个被选了的右括号,将其替代(清除前面的右括号的标记,并且标记新的右括号)
只要在这三种操作中取一个最优操作,就仍然可以保持当前最优。
那么,用一个for从头至尾循环,复杂度O(n),
对每个当前括号,做以上判断,
其中的往前找要求的括号的操作(2)和操作(3)可以用堆实现,复杂度O(logn)
一共O(nlogn)
代码://考场代码 因为可以水到分所以懒得写堆了(其实可以直接调用priority_queue 也很方便)
1 #include<algorithm> 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 using namespace std; 6 int s[200007]; 7 int q[200007]; 8 bool sf[200007]; 9 int a[200007]; 10 int b[200007]; 11 bool cmp(int qaq,int qwq) 12 { 13 return qaq>qwq; 14 } 15 int main() 16 { 17 int n; 18 cin>>n; 19 if(n<2001) 20 { 21 for(int i=1;i<=n;++i) 22 { 23 char x; 24 cin>>x; 25 if(x=='(')s[i]=1; 26 else s[i]=2; 27 } 28 for(int i=1;i<=n;++i) 29 { 30 cin>>q[i]; 31 //cout<<i<<" "<<s[i]<<" "<<a[i]<<endl; 32 if(s[i]==2) 33 { 34 int u=-99999999,pu=0; 35 for(int j=i-1;j;--j) 36 if(s[j]==1&&!sf[j]) 37 { 38 if(q[j]>u){u=q[j];pu=j;} 39 } 40 int v=99999999,pv=0; 41 for(int j=i-1;j;--j) 42 if(s[j]==2&&sf[j]) 43 { 44 if(q[j]<v){v=q[j];pv=j;} 45 } 46 //cout<<u<<" "<<pu<<" "<<v<<" "<<pv; 47 if(pu&&pv) 48 { 49 if(u+q[i]>=q[i]-v&&u+q[i]>0) 50 { 51 sf[pu]=1;sf[i]=1; 52 } 53 else if(q[i]-v>u+q[i]&&q[i]-v>0) 54 { 55 sf[pv]=0;sf[i]=1; 56 } 57 } 58 else 59 { 60 if(pu&&u+q[i]>0) 61 { 62 sf[pu]=1;sf[i]=1; 63 } 64 else if(pv&&q[i]-v>0) 65 { 66 sf[pv]=0;sf[i]=1; 67 } 68 } 69 //cout<<endl; 70 } 71 } 72 int ans=0; 73 for(int i=1;i<=n;++i) 74 if(sf[i]){ans+=q[i];} 75 cout<<ans;//强大怪!!! 76 return 0; 77 } 78 else 79 { 80 for(int i=1;i<=n;++i) 81 { 82 char x; 83 cin>>x; 84 if(x=='(')s[i]=1; 85 else s[i]=2; 86 } 87 int toa=0,tob=0; 88 for(int i=1;i<=n;++i) 89 { 90 int x; 91 scanf("%d",&x); 92 if(s[i]==1)a[++toa]=x; 93 else b[++tob]=x; 94 } 95 sort(a+1,a+toa+1,cmp); 96 sort(b+1,b+tob+1,cmp); 97 int ans=0; 98 for(int i=1;i<=min(toa,tob);++i) 99 { 100 if(a[i]+b[i]>0)ans+=(a[i]+b[i]); 101 else {cout<<ans;return 0;} 102 } 103 cout<<ans;//强大怪!!! 104 return 0; 105 } 106 } 107 //注释强大怪 你会rp++
UPD
$O(nlogn)$的堆写法↓
1 /* 2 qwerta 3 T36485 括号 Accepted 4 100 5 代码 C++,0.7KB 6 提交时间 2018-11-05 20:42:18 7 耗时/内存 442ms, 2124KB 8 */ 9 #include<iostream> 10 #include<cstdio> 11 #include<queue> 12 using namespace std; 13 char ch[200003]; 14 int v[200003]; 15 priority_queue<int,vector<int>,greater<int> >r;//r放右括号 16 priority_queue<int>l;//l放左括号 17 int main() 18 { 19 //freopen("a.in","r",stdin); 20 ios::sync_with_stdio(false); 21 int n; 22 cin>>n; 23 for(int i=1;i<=n;++i) 24 cin>>ch[i]; 25 for(int i=1;i<=n;++i) 26 cin>>v[i]; 27 // 28 int ans=0; 29 for(int i=1;i<=n;++i) 30 { 31 if(ch[i]=='(') 32 { 33 l.push(v[i]); 34 } 35 else 36 { 37 int x,y; 38 if(l.empty())x=-1;//赋成-1不碍事儿 39 else x=v[i]+l.top(); 40 if(r.empty())y=-1; 41 else y=v[i]-r.top(); 42 if(x>=y&&x>=0)//找个没用过的左括号 43 { 44 ans+=x; 45 l.pop(); 46 r.push(v[i]); 47 } 48 else if(y>=x&&y>=0)//替代一个用过的右括号 49 { 50 ans+=y; 51 r.pop(); 52 r.push(v[i]); 53 } 54 } 55 } 56 cout<<ans; 57 return 0; 58 }