Description

外星人制造了超级电脑来思考生命, 宇宙, 以及世界万物的答案, 最后得出了 42 这个结果. 人们并不满意于这个答案, 于是再一次设计了超级电脑. 经历了 \(\pi\) 百万年的计算, 再一次得出了新的答案. 答案是由一些字符串组成的, 每个字符串都只包含 () 两种字符. 现在的问题是, 从这些字符串中选取一些, 按照一定的顺序连接起来, 使得构成一个合法的括号序列而且长度尽量长.

Input

本题存在多组数据, 请做到文件底结束, 任两组数据间存在一空行.

在每组数据中, 第一行一个正整数 \(n\), 表示字符串个数.

接下来 \(n\) 行每行一个字符串. 总长度不超过 \(10000\).

Output

对于每组数据, 输出生成的括号序列的最大长度.

Sample Input

3
()
(()
)

Sample Output

6

Data Range

对于所有的数据, 满足:\(1 \leq n \leq 1000\).

Explanation

贪心地讲, 我们发现一个括号串中间括号的分布对结果是没有影响的. 有影响的是一个括号中有几个开括号, 几个闭括号; 有多少个左边强行关闭 (即没有开括号配对的) 闭括号, 右边 多少个强行打开的开括号. 将这些值维护以后, 我们开始贪心.

膜拜 hld67890 口头 AC 题解......

优先选择全开的括号串, 最后选择全闭的括号串.

然后在半开半闭的括号串中 dp, 优先选择开的更多的.

然而我觉得应该直接贪心, 然后 sort 了一下进行了一个 dp 过了, 但是并不明白怎么回事 QwQ

Source Code


#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>

#define maximize(__x,__y) __x=max(__x,__y)
#define max_3(__x,__y,__z) max(max(__x,__y),__z)
using namespace std;
typedef long long lli;
const int maxn = 1010, maxm = 10010;
const int infinit = 0x3f3f3f3f;

struct parthstr { int lbrac, rbrac, pairs; }; // Parentheses in strings
bool operator < (parthstr a, parthstr b) {
    bool ba = a.lbrac >= a.rbrac,
         bb = b.lbrac >= b.rbrac;
    if (ba != bb) {
        return ba > bb;
    } else {
        if (ba) return a.rbrac < b.rbrac;
        else return a.lbrac > b.lbrac;
    }
    return false;
} parthstr ps[maxn];

int n;
char str[maxm];
int dp[2][maxm];

int main(int argc, char** argv)
{
    while (scanf("%d", &n) != EOF) {
        // Clearing previous data for intactness.
        memset(ps, 0, sizeof(ps));
        memset(str, 0, sizeof(str));
        memset(dp, 0, sizeof(dp));
        // Read the strings and abandon the rest.
        int sumlen = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%s", str + 1);
            int len = strlen(str + 1);
            int lbrac = 0, // Unclosed left parentheses
                rbrac = 0, // Unclosed right parentheses
                pairs = 0; // Created pairs
            for (int j = 1; j <= len; j++) {
                if (str[j] == '(') {
                    lbrac += 1;
                } else {
                    if (lbrac == 0)
                        rbrac += 1;
                    else
                        lbrac -= 1, pairs += 1;
                }
            }
            pairs += rbrac;
            ps[i].lbrac = lbrac;
            ps[i].rbrac = rbrac;
            ps[i].pairs = pairs;
            sumlen += len;
        }
        // We need to arrange them in a timely manner so that
        // there is some part of greedy in it.
        sort(ps + 1, ps + n + 1);
        // To dynamic programming or not, this is a question
        int mx = (sumlen + 1) / 2;
        int now = 1, last = 0;
        dp[now][0] = 0;
        for (int j = 1; j <= mx; j++)
            dp[now][j] = -infinit;
        swap(now, last);
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= mx; j++)
                dp[now][j] = dp[last][j];
            int lbrac = ps[i].lbrac,
                rbrac = ps[i].rbrac,
                pairs = ps[i].pairs;
            for (int j = rbrac; j + lbrac - rbrac <= mx && j <= mx; j++) {
                maximize(dp[now][j + lbrac - rbrac], dp[last][j] + pairs);
            }
            swap(now, last);
        }
        // Now getting result.
        int res = dp[last][0] * 2;
        printf("%d\n", res);
    }
    return 0;
}