@bzoj - 4922@ [Lydsy1706月赛]Karp-de-Chant Number


@description@

卡常数被称为计算机算法竞赛之中最神奇的一类数字,主要特点集中于令人捉摸不透,有时候会让水平很高的选手迷之超时。
普遍认为卡常数是埃及人Qa'a及后人发现的常数。也可认为是卡普雷卡尔(Kaprekar)常数的别称。主要用于求解括号序列问题。
据考证,卡(Qa'a)是古埃及第一王朝的最后一位法老。他发现并研究了一种常数,后世以他的名字叫做卡常数。卡特兰数的起源也是因为卡的后人与特兰克斯结婚,生下来的孩子就叫卡特兰,而他只是发表了祖传的家书而已。Sereja也是卡的后人,提出括号序列问题,也是从家书里得到的资料。然而Sereja为了不让这个秘密公开,于是隐瞒了这道题的真正做法。可是由于卡的后人不是各个都像卡特兰一样爱慕虚荣,这一算法也无法找到。“欲见贤人而不以其道,犹欲其入而闭之门也”。卡之常数的奥秘,需要以一颗诚心去追寻。
【以上全是瞎 BB,不过还蛮有意思的 www】

现给定n个括号序列,你需要选择若干序列,将它们按一定的顺序从左往右拼接起来,得到一个合法的括号序列。
显然,这个问题可以用卡常数解决,为了检验你是否会卡常数,请写一个程序,计算可以得到的合法的括号序列的长度的最大值。

input
第一行包含一个正整数n(1<=n<=300),表示括号序列的个数。
接下来n行,每行一个长度在[1,300]之间的括号序列,仅由小括号构成。

output
输出一行一个整数,即最大长度,注意你可以一个序列也不选,此时长度为0。

sample input
3
())
((()
)()
sample output
10
**sample explain **:
按{2,1,3}的顺序拼接得到((()()))(),总长度为10。

@solution@

首先有个小性质:假如在不拼接的情况,一个括号已经有了匹配,那么拼接后这个括号的匹配不会变化。可以根据我们做括号匹配的过程理解这个性质。
所以基于此,我们可以把串中能匹配的括号先匹配完,剩下的部分一定是形如 “)))))....))(((....(((” 的样子。
我们将这部分记为 (p, q),其中 p 是 ')' 的个数,q 是 '(' 的个数。

考虑选了一些括号以后,怎么排列它们是最优的。可以用贪心来解决。
我们采用交换论证的方法来寻找排列方法。

假如 a,b 是相邻的且 a 在 b 前面,我们可以先将 a 的 '(' 和 b 的 ')' 匹配完,剩下新的一个 “)))(((” 型的串。
感性理解一下,我们应该剩下的东西越少越好。又因为 a 与 b 的括号总数不变,我们应该让 a 与 b 匹配尽量多的括号。匹配的括号个数就应该 \(=\min\{(个数,)个数\}\)

所以如果 \(\min\{a的(个数,b)个数\} > \min\{b的(个数, a的)个数\}\),则 a 就排在 b 前面。

可以发现上面那个东西是一直存在的,不会因为我们选的括号不同而变化。
所以我们可以先按照上面的法则排序,然后作一个简单的 dp 就可以了。

dp 的定义,以及状态转移留作习题。

@accepted code@

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 300;
const int INF = 1<<30;
struct node{
	int p, q;
	int num;
}a[MAXN + 5];// p -> ')', q -> '('
bool operator < (node a, node b) {
	return min(a.q, b.p) > min(a.p, b.q);
}
char s[MAXN + 5];
int dp[2][MAXN*MAXN + 5];
int main() {
	int n; scanf("%d", &n);
	for(int i=1;i<=n;i++) {
		scanf("%s", s);
		int len = strlen(s);
		for(int j=0;j<len;j++) {
			if( s[j] == '(' ) a[i].q++;
			else {
				if( !a[i].q ) a[i].p++;
				else a[i].q--, a[i].num += 2;
			}
		}
	}
	int tot = 0; sort(a+1, a+n+1);
	for(int i=1;i<=n;i++) {
		for(int j=0;j<=tot;j++)
			dp[i&1][j] = dp[i&1^1][j];
		for(int j=tot+1;j<=tot+a[i].q;j++)
			dp[i&1][j] = -INF;
		for(int j=a[i].p;j<=tot;j++)
			dp[i&1][j-a[i].p+a[i].q] = max(dp[i&1][j-a[i].p+a[i].q], dp[i&1^1][j]+2*a[i].p+a[i].num);
		tot += a[i].q;
	}
	printf("%d\n", dp[n&1][0]);
}

@details@

这道题让我想起了多校赛的某道题 HDU - 6299
当初好像是随便乱蒙了一个贪心性质就过了 www。

posted @ 2018-12-27 13:52  Tiw_Air_OAO  阅读(199)  评论(0编辑  收藏  举报