2018 Multi-University Training Contest 1 02
题意
给 \(n\) 个括号串。
问将 \(n\) 个串排列后首位相接,最多能匹配多少对括号。
\(1\ \leq\ n,\ |S|\ \leq\ 10^5\)
做法1
对于每个串 \(s\) 先把串内匹配的括号对删去,变成 \(a\) 个 \()\) 接上 \(b\) 个 \((\) 的形式。然后进行排序。
可以通过归纳法证明括号串 \(s\) 的匹配括号数量为 \(|s|\ -\ sum\ +\ 2mn\),其中 \(mn,\ sum\) 分别表示写成 \(1,\ -1\) 后的前缀最小值和总和。由于 \(len,\ sum\) 是定值,所以要做的就是最大化 \(mn\)。
注意直接这样写是错的。
bool cmp(int i, int j) {
return min(-a[i], -a[i] + b[i] - a[j]) > min(-a[j], -a[j] + b[j] - a[i]);
}
而应该进行分类讨论。右括号数量比左括号多的排在左括号数量比右括号多的后面。
如果同为左括号数量比右括号数量多,则按照右括号数量从小到大排序。可以通过 \(mn\) 值来证明。
如果同为右括号数量比左括号数量多,则按照左括号数量从大到小排序。可以通过前后匹配来证明。
代码
#include <bits/stdc++.h>
#ifdef DEBUG
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define debug(...)
#endif
#ifdef __WIN32
#define LLFORMAT "I64"
#else
#define LLFORMAT "ll"
#endif
using namespace std;
const int maxn = 1e5 + 10;
int n, a[maxn], b[maxn], c[maxn];
char s[maxn];
bool cmp(int i, int j) {
if(b[i] > a[i] && b[j] <= a[j]) return 1;
if(b[i] <= a[i] && b[j] > a[j]) return 0;
if(b[i] <= a[i] && b[j] <= a[j]) return b[i] > b[j];
return a[i] < a[j];
}
void solve() {
scanf("%d", &n);
int ans = 0;
for (int i = 0; i < n; ++i) {
c[i] = i;
scanf("%s", s);
int &a = ::b[i], &b = ::a[i];
for (int j = a = b = 0; s[j]; ++j) {
if(s[j] == '(') ++a;
else { if(a) --a, ++ans; else ++b; }
}
}
sort(c, c + n, cmp);
for (int s = 0, x, i, ii = 0; ii < n; ++ii) {
i = c[ii];
x = min(s, a[i]);
ans += x;
s -= x;
s += b[i];
}
printf("%d\n", ans * 2);
return;
}
int main() {
int T;
scanf("%d", &T);
while(T--) solve();
return 0;
}