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;
}
posted @ 2018-09-08 09:29  King_George  阅读(138)  评论(0编辑  收藏  举报