@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。

最新文章

  1. middleware - bodyparser
  2. Natural language style method declaration and usages in programming languages
  3. div模拟的下拉框特效
  4. 活动 Activity 四种加载模式
  5. Uploadify 3.2 not work in IE10
  6. I.MX6 android shutdown 内核崩溃
  7. linux下svn服务搭建
  8. POJ 2513 Colored Sticks 字典树、并查集、欧拉通路
  9. C# this.Invoke()的作用和用法(摘)
  10. 写给C语言新手的话
  11. 前台ajax加载数据
  12. java web开发时的绝对路径与相对路径
  13. C#常用单元测试框架比较:XUnit, NUnit, 和 Visual Studio(MSTest)
  14. 抄一篇maven的备忘
  15. 『Python』源码解析_源码文件介绍
  16. 如何将 asp.net core 应用进行 docker 容器部署
  17. C#: 执行批处理文件(*.bat)的方法
  18. .NET泛型02,泛型的使用
  19. 微信小程序中用户唯一ID的获取
  20. js 以函数名作为参数动态执行 函数

热门文章

  1. Spring Bean 作用域
  2. Intent 传递Map数据
  3. 独立版的 Asio安装与使用
  4. vue自定义指令之拖动页面的元素
  5. phpstorm服务器配置
  6. 安装 cx_Oracle
  7. nodejs request模块用法
  8. cnn.py cs231n
  9. HTTP请求响应头信息
  10. Leetcode868.Binary Gap二进制间距