HDU 6299 Balanced Sequence 【贪心】

2018 Multi-University Training Contest 1  1002

Balanced Sequence

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1469    Accepted Submission(s): 155

Problem Description

Chiaki has n strings s1,s2,…,sn consisting of '(' and ')'. A string of this type is said to be balanced:

+ if it is the empty string
+ if A and B are balanced, AB is balanced,
+ if A is balanced, (A) is balanced.

Chiaki can reorder the strings and then concatenate them get a new string t. Let f(t) be the length of the longest balanced subsequence (not necessary continuous) of t. Chiaki would like to know the maximum value of f(t) for all possible t.

Input

There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
The first line contains an integer n (1≤n≤105) -- the number of strings.
Each of the next n lines contains a string si (1≤|si|≤105) consisting of `(' and `)'.
It is guaranteed that the sum of all |si| does not exceeds 5×106.

Output

For each test case, output an integer denoting the answer.

Sample Input

2

1

)()(()(

2

)

)(

Sample Output

4

2

 


题意:输入一个T,接下来T组数据,每组数据输入一个 n,表示接下来 输入 n 个字符串,这些字符串只包含 '(' 和 ')' 

现在这些字符串可以重新排序(注意:是把字符串排序,不是把字符串里的字符排序),问重新排序后的字符串里最长可以构成多长的合法子串,因为是子串,所以这必然是要贪心取最长的

什么是合法的子串呢? 就是符合括号规则的子串,  (())   (((()))) 是合法的         )(   是不合法的

首先看第一个样例  )()(()(    这个字符串里存在两个 () () 所以合法长度是4

再看第二个样例  )  和 )(  重新排序成  )() 之后,合法长度就是 2 了

那么怎么贪心是最优的呢?

考虑一下,当你把你所有的字符串中包含的合法子串取出来后,剩余的串只会存在三种状态

1. (((((((((     全部是左括号

2. ))))))(((((((((     左边全是右括号,右边全是左括号

3. )))))))))))    全部是右括号

那么现在将这三种情况的残留子串做个重新排序的话,就能创造更多的合法子串

首先自然不用想都知道,第一种情况一定要全部放在左边才能创造价值,第三种情况要全部放在最右边才能创造价值

而第二种情况要放在中间,但是放在中间也是需要决策的

首先第二种子串放在中间的时候,它的右括号会和它左边的左括号创造价值,它的左括号会和右边的右括号创造价值,所以我们要尽量地让左括号多的出现在左边,右括号尽量多的出现在右边

所以这里引入一个定义,就是左括号的贡献,一个2类型的串里,如果左括号比右括号多,则左括号做正贡献,反则做负贡献

我们的贪心手段是,在2类型子串中,左括号做正贡献的放左边,在左括号做正贡献的串中,右括号少的放左边(因为要使右括号尽可能多的放在右边),如果左括号做负贡献(右括号比左括号多),那么应该左括号多的放左边(让左括号尽量放左边)

满足以上决策的就是最佳策略了

做法:预处理取出所有串中的合法串之后,把剩余串做分类,分别放进三个 动态数组中,然后对二类串做以上的贪心手段(就是使用对应策略的排序方法),做完之后,就可以按顺序去模拟地从剩余串中统计可以获得的价值了

代码如下:

#include
#include
#include
#include
#include
#include
#include
using namespace std;

char str[100005];

struct node{
	int left,right,l_r;	  // l_r 表示 left - right ,即差值 
}nod[100005];

stack sta;
vector v1,v2,v3;

bool cmp(node a,node b){
	if(a.l_r >= 0 && b.l_r >= 0){	// 左括号都做正贡献的时候,右括号少的放左边 
		return a.right < b.right;
	}else if(a.l_r >= 0){			// 左括号做正贡献的放左边 
		return 1;
	}else if(b.l_r >= 0){
		return 0;
	}else{
		return a.left > b.left;		// 左括号做负贡献的话,左括号多的放左边 
	}
}

void init(){
	v1.clear();
	v2.clear();
	v3.clear();
}

int main(){
	int t,n;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		int ans = 0;
		init();
		for(int i = 1;i <= n;i++){
			scanf("%s",str);
			int len = strlen(str);
			for(int j = 0;j < len;j++){
				if(j == 0){
					sta.push(str[j]);
					continue;
				}
				if(str[j] == '('){
					sta.push('(');
				}else{
					if(!sta.empty() && sta.top() == '('){
						sta.pop();
						ans++;
					}else{
						sta.push(')');
					}
				}
			}
			nod[i].left = 0;
			nod[i].right = 0;
			while(!sta.empty()){
				if(sta.top() == '('){
					nod[i].left++;
				}else{
					nod[i].right++;
				}
				sta.pop();
			}
			nod[i].l_r = nod[i].left - nod[i].right;
			if(nod[i].left > 0 && nod[i].right == 0){
				v1.push_back(nod[i]);
			}else if(nod[i].left == 0 && nod[i].right > 0){
				v3.push_back(nod[i]);
			}else if(nod[i].left > 0 && nod[i].right > 0){
				v2.push_back(nod[i]);
			}
		}
		if(v2.size() > 1)
			sort(v2.begin(),v2.end(),cmp);
		int left = 0;
		for(int i = 0;i < v1.size();i++){
			left += v1[i].left;
		}
		for(int i = 0;i < v2.size();i++){
			int tmp = min(left,v2[i].right);
			ans += tmp;
			left = left - tmp + v2[i].left;
		}
		for(int i = 0;i < v3.size();i++){
			int tmp = min(left,v3[i].right);
			ans += tmp;
			left -= tmp;
			if(left == 0)
				break;
		}
		printf("%d\n",ans * 2);
	}
	return 0;
}

 

你可能感兴趣的:(ACM-,模拟,字符串,思维题,贪心)