bzoj3797

3797: Ural 1745 Yet Another Answer

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值