3797: Ural 1745 Yet Another Answer
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 155 Solved: 37
[ Submit][ Status][ Discuss]
Description
外星人制造了超级电脑来思考生命,宇宙,以及世界万物的答案,最后得出了42这个结果。人们并不满意于这个答案,于是再一次设计了超级电脑。经历了π百万年的计算,再一次得出了新的答案。
答案是由一些字符串组成的,每个字符串都只包含“(”和“)”两种字符。
现在的问题是,从这些字符串中选取一些,按照一定的顺序连接起来,使得构成一个合法的括号序列而且长度尽量长。
Input
本题存在多组数据,请做到文件底结束,任两组数据间存在一空行
第一行正整数 N (1 ≤ N ≤ 1,000).表示字符串个数。
接下来N行每行一个字符串。总长度不超过10,000。(编号1到N)
Output
对于每组数据输出最大长度
Sample Input
3
()
(()
)
()
(()
)
Sample Output
6
HINT
样例解释
(())()
题解:
花式dp。。。
如果每个字符串顺序确定,只能选择是否要选的话就是简单dp了,记录当前左括号剩了几个就可以了。
但是现在顺序不确定。通过观察,发现最开始一定是一堆"(((("最后是")))"。
设一个串把括号配对后是a个左括号和b个右括号。那么猜想先放一些a>b的串,然后放b>a的串。
所以一共有4类串,排好序按照顺序dp就可以了。
对于a>b的串,按照a升序排序,直观想想因为开始可用左括号少,如果右括号太多就可能不能匹配。而每加一个串可用左括号都会变多,就可能可以加上a较大的串了。
对于a<b的串,按照a降序排序,原因类似,可用左括号会越来越少。
搜ural1745有人给了一个证明。
手抖导致数组开大,贡献一次MLE。。。
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int maxn = 1200;
struct so {
int l , r , v , f;
} s[maxn];
int n;
char z[12000];
int dp[maxn][5100];
bool cmp ( so x1 , so x2 ) {
if ( x1.f == x2.f ) {
if ( x1.f == 1 ) return x1.l < x2.l;
if ( x1.f == 2 ) return x1.l > x2.l;
if ( x1.f == 3 ) return x1.l > x2.l;
}
return x1.f < x2.f;
}
void clear () {
int i;
for ( i = 1 ; i <= n ; i++ ) s[i].l = s[i].r = s[i].v = 0;
}
void work () {
int i , j , len , t;
clear ();
for ( i = 1 ; i <= n ; i++ ) {
scanf ( "%s" , z + 1 );
len = strlen ( z + 1 );
t = 0;
for ( j = 1 ; j <= len ; j++ ) {
if ( z[j] == '(' ) t++;
else t--;
s[i].l = min ( s[i].l , t );
}
s[i].l = -s[i].l;
t = 0;
for ( j = len ; j >= 1 ; j-- ) {
if ( z[j] == '(' ) t++;
else t--;
s[i].r = max ( s[i].r , t );
}
s[i].v = len;
if ( s[i].l < s[i].r ) s[i].f = 1;
if ( s[i].l >= s[i].r ) {
s[i].f = 2;
if ( s[i].r == 0 ) s[i].f = 3;
}
}
sort ( s + 1 , s + 1 + n , cmp );
//for ( i = 1 ; i <= n ; i++ )
// printf ( "%d %d %d\n" , s[i].l , s[i].r , s[i].f );
dp[0][0] = 0;
for ( i = 1 ; i <= 5000 ; i++ ) dp[0][i] = -9999999;
for ( i = 1 ; i <= n ; i++ ) {
for ( j = 0 ; j <= 5000 ; j++ ) dp[i][j] = dp[i-1][j];
for ( j = s[i].l ; j <= 5000 ; j++ ) {
if ( j - s[i].l + s[i].r > 5000 ) break;
dp[i][j-s[i].l+s[i].r] = max ( dp[i][j-s[i].l+s[i].r] , dp[i-1][j] + s[i].v );
}
//for ( j = 0 ; j <= 20 ; j++ ) printf ( "%d " , dp[i][j] );
//printf ( "\n" );
}
printf ( "%d\n" , dp[n][0] );
}
int main () {
while ( scanf ( "%d" , &n ) != EOF ) work ();
return 0;
}