題目
正解
左括號記爲,右括號記爲。
首先答案顯然等於
隔壁大佬的方法有一部分聽得不是很懂,所以這裏就只將gmh77的方法。
考慮這樣的模型:有個二元組,表示當前的值如果大於等於,那麼就會得到。
有個經典問題:如果有若干個,用什麼順序選取,才能使在滿足值一直大於等於的情況下,一開始所需要的值最小。
打怪獸問題:HDU6326
這個東西可以貪心:定義的優劣關係:
- 大於等於的比小於的優。
- 大於等於時,越小越優。
- 小於時,越大越優。
前面兩條都比較顯然,但第三條看起來不太好懂。
一種解釋是隔壁所云“反過來”:將操作順序以及符號反過來,原來可以看做先減再加,反過來就變成減加,記作。這時候是正的,於是按照從小到大倒着往前做,就相當於按從大到小順着往後做。
或者有一種更加舒服的化學解釋方法:
把看做熱量,當時,可以看做正在發生吸熱反應。
表示發生反應的最低能量。於是可以理解成活化能。
發生反應之後,活化能化爲內能。爲了支持後面的反應,轉化爲內能的活化能儘量要爲後面的反應提供能量,使其達到反應發生的最低能量。
爲了讓活化能不要浪費,按照從大到小的順序反應。
於是題目中的每條序列都可以視作若干個這樣的二元組。
可以發現,當一個劣的二元組在優的二元組前的時候,操作完劣的二元組之後肯定會立即執行優的二元組。於是把這些二元組合並起來,最終就會形成一個從優到劣的二元組。
兩個序列之間按照開頭的優劣順序貪心即可。
題解講得可能更清楚。
代碼
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 1000010
int n,m;
char s[N],t[N];
struct Info{
int a,b;
} da[N],db[N];
bool cmp(Info x,Info y){
if (x.b>=0 && y.b>=0)
return x.a<y.a;
if (x.b<0 && y.b<0)
return x.a+x.b>y.a+y.b;
return x.b>y.b;
}
Info operator+(Info x,Info y){
return {-min(x.b-y.a,min(-x.a,-y.a)),x.b+y.b};
}
void calc(Info &x,Info y){
if (y.a>x.b){
x.a+=y.a-x.b;
x.b=y.a;
}
x.b+=y.b;
}
int ak,bk;
void init(char s[],int n,Info d[],int &k){
for (int i=1;i<=n;++i)
s[i]=(s[i]=='('?1:-1);
k=0;
for (int i=1;i<=n;++i){
Info nw={max(-int(s[i]),0),s[i]};
while (k && cmp(nw,d[k])){
nw=d[k]+nw;
--k;
}
d[++k]=nw;
}
}
int main(){
freopen("irony.in","r",stdin);
freopen("irony.out","w",stdout);
int T;
scanf("%d",&T);
while (T--){
scanf("%d%d%s%s",&n,&m,s+1,t+1);
init(s,n,da,ak);
init(t,m,db,bk);
int cnt=0;
for (int i=1;i<=n;++i)
cnt+=s[i];
for (int i=1;i<=m;++i)
cnt+=t[i];
int i=1,j=1;
Info ans={0,0};
while (i<=ak && j<=bk)
if (cmp(da[i],db[j]))
calc(ans,da[i++]);
else
calc(ans,db[j++]);
for (;i<=ak;++i)
calc(ans,da[i]);
for (;j<=bk;++j)
calc(ans,db[j]);
printf("%d\n",cnt+2*ans.a);
}
return 0;
}