首先考虑下面的问题: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. CSP-S 2019 Day 2 T3 树的重心

    CSP-S 2019 Day 2 T3 树的重心 题 给出了一个大小为\(n\)的树,树中结点从 1∼n 编号.小简单的课后作业是求出这棵树单独删去每条边后,分裂出的两个子树的重心编号和之和. \(n ...

  2. writing-mode,文字竖直书写,字符之间距离,单词之间距离

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  3. Thinkphp [美味]常用代码

    //调试开关 function _initialize () { // 调试开关 C ( 'SHOW_PAGE_TRACE', TRUE ); } //判断 IS_AJAX && $t ...

  4. 移动web--移动屏幕适配-完整的viewport设置

  5. 你需要一个新的model实体的时候必须new一个.奇怪的问题: 使用poi解析Excel的把数据插入数据库同时把数据放在一个list中,返回到页面展示,结果页面把最后一条数据显示了N次

    数据库显示数据正常被插 插入一条打印一次数据,也是正常的,但是执行完,list就全部变成了最后一条数据.很奇怪 单步调试 给list插入第一条数据 model是6607 连续插了多条数据都是6607 ...

  6. GYM 101981E(开关反转性质)

    要点 做法是删去连续的k个0或k个1,连消.消消乐的那种,网上博主用个栈\(O(n)\)就很优秀地操作了这个过程 原因是有性质:比如k=3,101000贪心地翻就能翻成000101,所以连续的k个可以 ...

  7. winform应用程序异常处理

    对于winform应用程序补抓异常信息,我们经常用到得try catch. 如果代码中在某个地方执行异常,但是没有加try catch,这个时候就需要做一些全局异常捕捉. 怎么做到全局异常捕捉.win ...

  8. H5C3--语义标签以及语义标签IE8兼容,表单元素新属性,度量器,自定义属性,dataList,网络监听,文件读取

    HTML5新增标签以及HTML5新增的api     1.H5并不是新的语言,而是html语言的第五次重大修改--版本     2.支持:所有的主流浏览器都支持h5.(chrome,firefox,s ...

  9. MySQL ODBC驱动安装和配置数据源

    一.MySQL的ODBC驱动下载及安装 步骤一:下载ODBC驱动安装包 1.下载地址: https://dev.mysql.com/downloads/connector/odbc/ 2.选择适合自己 ...

  10. 笔试面试记录-字符串转换成整型数等(aatoi,itoa)

    C语言中经常用到字符串与数字之间的相互转换,常见的此类库函数有atof(字符串转换成浮点数).atoi(字符串转换成整型数).atol(字符串转换成长整形).itoa(整型数转换成字符串).ltoa( ...