首先考虑下面的问题:Code[VS] 3657

 我们用以下规则定义一个合法的括号序列:

    (1)空序列是合法的

    (2)假如S是一个合法的序列,则 (S) 和[S]都是合法的

    (3)假如A 和 B 都是合法的,那么AB和BA也是合法的

    例如以下是合法的括号序列:

      (), [], (()), ([]), ()[], ()[()]

    以下是不合法括号序列的:

      (, [, ], )(, ([]), ([()

  现在给定一些由'(', ')', '[', ,']'构成的序列 ,请添加尽量少的括号,得到一个合法的括号序列。

  输入包括号序列S。含最多100个字符(四种字符: '(', ')', '[' and ']') ,都放在一行,中间没有其他多余字符。

  使括号序列S成为合法序列需要添加最少的括号数量。

  样例输入 Sample Input

   ([()  

  样例输出 Sample Output

   2

  这是LRJ黑书上讲动态规划的第一道例题,我觉得不算简单>_<.,但让我明白了动态规划的两种动机:记忆化搜索和自底向上的递推。先说这道题的理解,黑书上设SiSi+1...Sj最少需要添加d[i,j]个括号。当S是'(S)'或'[S]'形式是很好理解,由于两端是对称的,那么我可以递归考虑里面的:d[i,j]=d[i+1,j-1]。当S是'(S'、'[S'、'S)'、'S]'等类似前面的道理,只不过考虑的分别是d[i,j]=d[i+1,j],d[i,j]=d[i,j-1]。其实让我迷惑的是最后还要把S分成两段Si....Sk,Sk+1...Sj分别求解再相加。后来看了别人博客的解释才明白些。因为S就可以只看成两类,两段匹配的和不匹配的,匹配的直接递归,不匹配的就分成两部分再求解。

所以针对上面的问题,就有了两种dp写法。dp[i][j]表示i、j为开头结尾时需要添加的括号数。

  记忆化搜索:参考代码如下。黑书里面写的有我注释那那部分,但是按照上面的分析,其实直接分成dp[i][j]=dp[i+1][j-1]和dp[i][j]=dp[i][k]+dp[k+1][j]就可以了。但是我觉得那部分代码对我理解递归还是个很有帮助的,而且不注释好像更快些。Recursive is amazing!  

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = ;
int dp[MAXN][MAXN];
char s[MAXN]; int dfs(int i, int j)
{
if (dp[i][j] != -) return dp[i][j];
if (i > j) return ;
if (i == j) return ;
int ans = 1e9;
if ((s[i] == '('&&s[j] == ')') || (s[i] == '['&&s[j] == ']'))
ans = min(ans, dfs(i + , j - ));
/*else if (s[i] == '(' || s[i] == '[')
ans = min(ans, dfs(i + 1, j) + 1);
else if (s[j] == ')' || s[j] == ']')
ans = min(ans, dfs(i, j - 1) + 1);*/
for (int k = i; k < j; k++)
ans = min(ans, dfs(i, k) + dfs(k + , j));
return dp[i][j] = ans;
} int main()
{
while (scanf("%s",s)==)
{
int len = strlen(s);
memset(dp, -, sizeof(dp));
printf("%d\n", dfs(, len - ));
}
}

 自底向上的递推:其实看起来代码和上面的差不多。也是一样,去掉注释竟然会快些。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = ;
int dp[MAXN][MAXN];
char s[MAXN]; int main()
{
while (scanf("%s",s)==)
{
int len = strlen(s);
for (int i = ; i < len; i++) {
dp[i][i] = , dp[i][i - ] = ;
}
for (int p = ; p < len; p++)//p指的是i、j之间的距离
{
for (int i = ; i + p < len; i++)
{
int j = i + p;
dp[i][j] = 1e9;
if ((s[i] == '('&&s[j] == ')') || (s[i] == '['&&s[j] == ']'))
dp[i][j] = dp[i + ][j - ];
/* else if (s[i] == '(' || s[i] == '[')
dp[i][j] = min(dp[i][j], dp[i + 1][j])+1;
else if (s[j] == ')' || s[j] == ']')
dp[i][j] = min(dp[i][j], dp[i][j - 1])+1; */
for (int k = i; k < j; k++)
dp[i][j] = min(dp[i][j], dp[i][k]+dp[k+][j]);
}
}
printf("%d\n", dp[][len - ]);
}
return ;
}

下面的UVa 1626 poj 1141就是比上面的题多了个输出结果,这俩题一样,就是输入输出要求有点差别而已。需要特别注意的是,这两道题输入中都有空格,所以只能用gets()函数,我用scanf("%s")WA到死。。。(也没见题中说由空格啊!有空格还对吗?难道空格是在字符串开头?)

其实一看见让输出我是很懵逼的,这怎么输出。能求出最少添加数我就很开心了。下面说说自己的理解,别人的方法是递归输出。既然是递归输出,就先考虑一下边间,显然i>j时直接return.i==j时,是'('或')'输出'()'否则输出'[]',当i!=j时,若i,j两端点正好匹配,那就先输出左端点再递归输出i+1,j-1部分最后输出又端点,若是剩下的其他情况就像上面一样分成两部分判断继续递归。这里分成两部分后应该在哪里分开递归?自然是在更新dp[i][j]=dp[i][k]+dp[k+1][j]的地方,网上有人在dp时添加了另外一个数组记录这个位置,也有人没有添加,而是递归输出结果的时候再判断,我这里选择了第二种,代码看起来简洁些。

自底向上递推:为了方便把端点匹配的情况写成了一个函数。有一个点就是注释里的dp[i][j]==dp[i+1][j-1]不能少,否则会WA!感觉应该是虽然有可能i,j匹配,但这不是原序列中i、j对应的匹配,因为这时候是在递归,所以不加上会WA。估计我比赛的时候不会注意到这种细节。。。。但另开个数组记录就不用考虑了。

UVa 1626

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=;
int dp[MAXN][MAXN];
char s[MAXN]; bool Judge(int i,int j)
{
if(s[i]=='('&&s[j]==')') return ;
if(s[i]=='['&&s[j]==']') return ;
return ;
} void Print(int i,int j)
{
if(i>j) return;
if(i==j){
if(s[i]=='('||s[j]==')') printf("()");
else printf("[]");
return;
}else if(Judge(i,j)&&dp[i][j]==dp[i+][j-]){//后面的判断条件不能省略
printf("%c",s[i]);
Print(i+,j-);
printf("%c",s[j]);
return;
}else for(int k=i;k<j;k++)
if(dp[i][j]==dp[i][k]+dp[k+][j]){
Print(i,k);
Print(k+,j);
return;
}
} int main()
{
int T;
scanf("%d",&T);
getchar();
while(T--)
{
gets(s);
gets(s);
int len=strlen(s);
memset(dp,,sizeof(dp));
for(int i=;i<len;i++){
dp[i][i]=,dp[i][i-]=;
}
for(int p=;p<len;p++)
{
for(int i=;i+p<len;i++)
{
int j=i+p;
dp[i][j]=1e9;
if(Judge(i,j))
dp[i][j]=dp[i+][j-];
for(int k=i;k<j;k++)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+][j]);
}
}
Print(,len-);
printf("\n");
if(T)
printf("\n");
}
return ;
}

附带一个用flag[]数组标记的写法:

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=;
int dp[MAXN][MAXN],flag[MAXN][MAXN];
char s[MAXN]; bool Judge(int i,int j)
{
if(s[i]=='('&&s[j]==')') return ;
if(s[i]=='['&&s[j]==']') return ;
return ;
} void Print(int i,int j)
{
if(i>j) return;
if(i==j){
if(s[i]=='('||s[j]==')') printf("()");
else printf("[]");
return;
}else if(flag[i][j]==-){
printf("%c",s[i]);
Print(i+,j-);
printf("%c",s[j]);
return;
}else {
Print(i,flag[i][j]);
Print(flag[i][j]+,j);
}
} int main()
{
int T;
scanf("%d",&T);
getchar();
while(T--)
{
gets(s);
gets(s);
int len=strlen(s);
memset(dp,,sizeof(dp));
for(int i=;i<len;i++){
dp[i][i]=,dp[i][i-]=;
}
for(int p=;p<len;p++)
{
for(int i=;i+p<len;i++)
{
int j=i+p;
dp[i][j]=1e9;
if(Judge(i,j)){
dp[i][j]=dp[i+][j-];
flag[i][j]=-;
}
for(int k=i;k<j;k++){
if(dp[i][j]>dp[i][k]+dp[k+][j]){
dp[i][j]=dp[i][k]+dp[k+][j];
flag[i][j]=k;
}
}
}
}
Print(,len-);
printf("\n");
if(T)
printf("\n");
}
return ;
}

UVa 1626 用flag[]标记

poj 1141类似:

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = ;
int dp[MAXN][MAXN];
char s[MAXN]; bool Judge(int i, int j)
{
if (s[i] == '('&&s[j] == ')') return true;
if (s[i] == '['&&s[j] == ']') return true;
return false;
} void Print(int l, int r)
{
if (l > r) return;
if (l == r) {
if (s[l] == '(' || s[r] == ')')
printf("()");
else printf("[]");
return;
}
else if (Judge(l, r) && dp[l][r] == dp[l + ][r - ]) {
printf("%c", s[l]);
Print(l + , r - );
printf("%c", s[r]);
}else for(int k=l;k<r;k++)
if (dp[l][r] == dp[l][k] + dp[k + ][r]) {
Print(l, k);
Print(k + , r);
break;
}
} int main()
{
while (gets(s))
{
int len = strlen(s);
memset(dp, , sizeof(dp));
for (int i = ; i < len; i++) {
dp[i][i - ] = , dp[i][i] = ;
}
for (int p = ; p < len; p++)
{
for (int i = ; i < len - p; i++) {
int j = i + p;
dp[i][j] = 1e9;
if ((s[i] == '('&&s[j] == ')') || (s[i] == '['&&s[j] == ']'))
dp[i][j] = dp[i + ][j - ];
for (int k = i; k < j; k++)
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + ][j]);
}
}
Print(, len - );
printf("\n");
}
return ;
}

自底向上递推poj1141

记忆化搜索也是类似的方法,比递推满了好多倍。。用flag[]数组标记比较好,不标记不知道怎么弄>_<。而且要像上面一样令int ans=1e9,最后再返回dp[i][j]=ans,直接令dp[i][j]=1e9会出错。。。先不深究了,这题写了一天。。。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = ;
int dp[MAXN][MAXN],flag[MAXN][MAXN];
char s[MAXN]; bool Judge(int i, int j)
{
if (s[i] == '('&&s[j] == ')') return ;
if (s[i] == '['&&s[j] == ']') return ;
return ;
} int dfs(int i, int j)
{
if (dp[i][j] != -) return dp[i][j];
if (i > j) return ;
if (i == j) return ;
int ans = 1e9;
if (Judge(i,j)) {
ans = min(ans, dfs(i + , j - ));
flag[i][j] = -;
}
for (int k = i; k < j; k++) {
if (ans > dfs(i, k) + dfs(k + , j)) {
ans = dfs(i, k) + dfs(k + , j);
flag[i][j] = k;
}
}
return dp[i][j] = ans;
} void Print(int i, int j)
{
if (i>j) return;
if (i == j) {
if (s[i] == '(' || s[j] == ')') printf("()");
else printf("[]");
return;
}
else if (flag[i][j] == -) {
printf("%c", s[i]);
Print(i + , j - );
printf("%c", s[j]);
return;
}
else {
Print(i, flag[i][j]);
Print(flag[i][j] + , j);
}
} int main()
{
int T;
scanf("%d", &T);
getchar();
while (T--)
{
gets(s);
gets(s);
int len = strlen(s);
memset(dp, -, sizeof(dp));
dfs(, len - );
Print(, len - );
printf("\n");
if (T)
printf("\n");
}
return ;
}

记忆化搜索Uva 1626

poj 1141的完全类似。。。

括号序列问题 uva 1626 poj 1141【区间dp】的更多相关文章

  1. UVA 1626 Brackets sequence 区间DP

    题意:给定一个括号序列,将它变成匹配的括号序列,可能多种答案任意输出一组即可.注意:输入可能是空串. 思路:D[i][j]表示区间[i, j]至少需要匹配的括号数,转移方程D[i][j] = min( ...

  2. [BZOJ 4350]括号序列再战猪猪侠 题解(区间DP)

    [BZOJ 4350]括号序列再战猪猪侠 Description 括号序列与猪猪侠又大战了起来. 众所周知,括号序列是一个只有(和)组成的序列,我们称一个括号 序列S合法,当且仅当: 1.( )是一个 ...

  3. POJ 1141 区间DP

    给一组小括号与中括号的序列,加入最少的字符,使该序列变为合法序列,输出该合法序列. dp[a][b]记录a-b区间内的最小值, mark[a][b]记录该区间的最小值怎样得到. #include &q ...

  4. poj 1141 区间dp+递归打印路径

    Brackets Sequence Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 30383   Accepted: 871 ...

  5. UVA 10003 Cutting Sticks 区间DP+记忆化搜索

    UVA 10003 Cutting Sticks+区间DP 纵有疾风起 题目大意 有一个长为L的木棍,木棍中间有n个切点.每次切割的费用为当前木棍的长度.求切割木棍的最小费用 输入输出 第一行是木棍的 ...

  6. POJ 2955 区间DP Brackets

    求一个括号的最大匹配数,这个题可以和UVa 1626比较着看. 注意题目背景一样,但是所求不一样. 回到这道题上来,设d(i, j)表示子序列Si ~ Sj的字符串中最大匹配数,如果Si 与 Sj能配 ...

  7. POJ 1141 经典DP 轨迹打印

    又几天没写博客了,大二的生活实在好忙碌啊,开了五门专业课,每周都是实验啊实验啊实验啊....我说要本月刷够60题,但好像完不成了,也就每天1题的样子.如今写动规还是挺有条理的,包括这道需要打印轨迹,其 ...

  8. Uva 10891 经典博弈区间DP

    经典博弈区间DP 题目链接:https://uva.onlinejudge.org/external/108/p10891.pdf 题意: 给定n个数字,A和B可以从这串数字的两端任意选数字,一次只能 ...

  9. uva 10003 Cutting Sticks(区间DP)

    题目连接:10003 - Cutting Sticks 题目大意:给出一个长l的木棍, 再给出n个要求切割的点,每次切割的代价是当前木棍的长度, 现在要求输出最小代价. 解题思路:区间DP, 每次查找 ...

随机推荐

  1. SpringMVC注解开发方式

    环境准备 springmvc版本:spring3.2 需要spring3.2所有jar(一定包括spring-webmvc-3.2.0.RELEASE.jar 工程结构 配置前端控制器(web.xml ...

  2. 超干货!Cassandra Java堆外内存排查经历全记录

    背景 最近准备上线cassandra这个产品,同事在做一些小规格ECS(8G)的压测.压测时候比较容易触发OOM Killer,把cassandra进程干掉.问题是8G这个规格我配置的heap(Xmx ...

  3. uva 10036

    10036 - Divisibility 额..直接复制不过来,只好叙述一下了...t组样例,n个数(1-10000),k(2-100)是要取余的数,然后给出n个数第一个数前不能加正负号,其他的数前面 ...

  4. phpstudy安装好之后mysql无法启动(亲测可行)

    安装好phpstudy后,Apache可以启动,Mysql无法启动. 尝试解决办法:可能是之前已经装过Mysql,要把系统服务里面的MySQL删除,留下MySQLa服务. 在cmd命令行下输入:sc ...

  5. ucore os 前初始化

    BIOS 初始化完成说起 连接的时候指定了 -Ttext 0x7c00 也指定了 -e start 所以booasm.S 中的start 就呗钦定为程序入口了. 开始就是 屏蔽中断 初始化段寄存器 使 ...

  6. linux实时系统监控工具mpstat

    mpstat (RHEL5默认不安装) mpstat是MultiProcessor Statistics的缩写,是实时系统监控工具.其报告与CPU的一些统计信息,这些信息存放在/proc/stat文件 ...

  7. redis的安装,以及主从实现同步

    Redis的主从复制功能非常强大,一个master可以拥有多个slave,而一个slave又可以拥有多个slave,如此下去,形成了强大的多级服务器集群架构.下面我演示下怎样在多台服务器上进行Redi ...

  8. Django项目:CRM(客户关系管理系统)--08--03PerfectCRM创建基本数据03

    如果感觉本章博客对您有帮助,请尽情打赏吧!

  9. visual studio code 调试reactjs

    1.首先到visual studio code官网下载ide. 2.打开visual studio code,点击右侧菜单条的小图标 找到[Debugger for Chrome],并安装 3.打开c ...

  10. [运维]Dell R710 raid配置 标签: raid运维 2017-04-15 19:35 581人阅读 评论(16)

    Dell R系列的一些服务器,raid的配置都大同小异,公司大部分的服务器,都是Dell R710型号的,这个型号的raid界面配置起来还是很简单的,下面来跟随小编体验一下raid如何配置吧.ps:图 ...