zoukankan      html  css  js  c++  java
  • 括号序列问题 uva 1626 poj 1141【区间dp】

    首先考虑下面的问题: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!  

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 const int MAXN = 107;
     7 int dp[MAXN][MAXN];
     8 char s[MAXN];
     9 
    10 int dfs(int i, int j)
    11 {
    12     if (dp[i][j] != -1) return dp[i][j];
    13     if (i > j) return 0;
    14     if (i == j) return 1;
    15     int ans = 1e9;
    16     if ((s[i] == '('&&s[j] == ')') || (s[i] == '['&&s[j] == ']'))
    17         ans = min(ans, dfs(i + 1, j - 1));
    18     /*else if (s[i] == '(' || s[i] == '[')
    19         ans = min(ans, dfs(i + 1, j) + 1);
    20     else if (s[j] == ')' || s[j] == ']')
    21         ans = min(ans, dfs(i, j - 1) + 1);*/
    22     for (int k = i; k < j; k++)
    23         ans = min(ans, dfs(i, k) + dfs(k + 1, j));
    24     return dp[i][j] = ans;
    25 }
    26 
    27 int main()
    28 {
    29     while (scanf("%s",s)==1)
    30     {
    31         int len = strlen(s);
    32         memset(dp, -1, sizeof(dp));
    33         printf("%d
    ", dfs(0, len - 1));
    34     }
    35 }

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

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 const int MAXN = 107;
     7 int dp[MAXN][MAXN];
     8 char s[MAXN];
     9 
    10 int main()
    11 {
    12     while (scanf("%s",s)==1)
    13     {
    14         int len = strlen(s);
    15         for (int i = 0; i < len; i++) {
    16             dp[i][i] = 1, dp[i][i - 1] = 0;
    17         }
    18         for (int p = 1; p < len; p++)//p指的是i、j之间的距离
    19         {
    20             for (int i = 0; i + p < len; i++)
    21             {
    22                 int j = i + p;
    23                 dp[i][j] = 1e9;
    24                 if ((s[i] == '('&&s[j] == ')') || (s[i] == '['&&s[j] == ']'))
    25                     dp[i][j] = dp[i + 1][j - 1];
    26             /*    else if (s[i] == '(' || s[i] == '[')
    27                     dp[i][j] = min(dp[i][j], dp[i + 1][j])+1;
    28                 else if (s[j] == ')' || s[j] == ']')
    29                     dp[i][j] = min(dp[i][j], dp[i][j - 1])+1;  */
    30                 for (int k = i; k < j; k++)
    31                     dp[i][j] = min(dp[i][j], dp[i][k]+dp[k+1][j]);
    32             }
    33         }
    34         printf("%d
    ", dp[0][len - 1]);
    35     }
    36     return 0;
    37 }

    下面的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

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 const int MAXN=107;
     7 int dp[MAXN][MAXN];
     8 char s[MAXN];
     9 
    10 bool Judge(int i,int j)
    11 {
    12     if(s[i]=='('&&s[j]==')') return 1;
    13     if(s[i]=='['&&s[j]==']') return 1;
    14     return 0;
    15 }
    16 
    17 void Print(int i,int j)
    18 {
    19     if(i>j) return;
    20     if(i==j){
    21         if(s[i]=='('||s[j]==')') printf("()");
    22         else printf("[]");
    23         return;
    24     }else if(Judge(i,j)&&dp[i][j]==dp[i+1][j-1]){//后面的判断条件不能省略
    25         printf("%c",s[i]);
    26         Print(i+1,j-1);
    27         printf("%c",s[j]);
    28         return;
    29     }else for(int k=i;k<j;k++)
    30     if(dp[i][j]==dp[i][k]+dp[k+1][j]){
    31         Print(i,k);
    32         Print(k+1,j);
    33         return;
    34     }
    35 }
    36 
    37 int main()
    38 {
    39     int T;
    40     scanf("%d",&T);
    41     getchar();
    42     while(T--)
    43     {
    44         gets(s);
    45         gets(s);
    46         int len=strlen(s);
    47         memset(dp,0,sizeof(dp));
    48         for(int i=0;i<len;i++){
    49             dp[i][i]=1,dp[i][i-1]=0;
    50         }
    51         for(int p=1;p<len;p++)
    52         {
    53             for(int i=0;i+p<len;i++)
    54             {
    55                 int j=i+p;
    56                 dp[i][j]=1e9;
    57                 if(Judge(i,j))
    58                     dp[i][j]=dp[i+1][j-1];
    59                 for(int k=i;k<j;k++)
    60                     dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
    61             }
    62         }
    63         Print(0,len-1);
    64         printf("
    ");
    65         if(T)
    66             printf("
    ");
    67     }
    68     return 0;
    69 }

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

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 const int MAXN=107;
     7 int dp[MAXN][MAXN],flag[MAXN][MAXN];
     8 char s[MAXN];
     9 
    10 bool Judge(int i,int j)
    11 {
    12     if(s[i]=='('&&s[j]==')') return 1;
    13     if(s[i]=='['&&s[j]==']') return 1;
    14     return 0;
    15 }
    16 
    17 void Print(int i,int j)
    18 {
    19     if(i>j) return;
    20     if(i==j){
    21         if(s[i]=='('||s[j]==')') printf("()");
    22         else printf("[]");
    23         return;
    24     }else if(flag[i][j]==-1){
    25         printf("%c",s[i]);
    26         Print(i+1,j-1);
    27         printf("%c",s[j]);
    28         return;
    29     }else {
    30         Print(i,flag[i][j]);
    31         Print(flag[i][j]+1,j);
    32     }
    33 }
    34 
    35 int main()
    36 {
    37     int T;
    38     scanf("%d",&T);
    39     getchar();
    40     while(T--)
    41     {
    42         gets(s);
    43         gets(s);
    44         int len=strlen(s);
    45         memset(dp,0,sizeof(dp));
    46         for(int i=0;i<len;i++){
    47             dp[i][i]=1,dp[i][i-1]=0;
    48         }
    49         for(int p=1;p<len;p++)
    50         {
    51             for(int i=0;i+p<len;i++)
    52             {
    53                 int j=i+p;
    54                 dp[i][j]=1e9;
    55                 if(Judge(i,j)){
    56                     dp[i][j]=dp[i+1][j-1];
    57                     flag[i][j]=-1;
    58                 }
    59                 for(int k=i;k<j;k++){
    60                     if(dp[i][j]>dp[i][k]+dp[k+1][j]){
    61                         dp[i][j]=dp[i][k]+dp[k+1][j];
    62                         flag[i][j]=k;
    63                     }
    64                 }
    65             }
    66         }
    67         Print(0,len-1);
    68         printf("
    ");
    69         if(T)
    70             printf("
    ");
    71     }
    72     return 0;
    73 }
    UVa 1626 用flag[]标记

    poj 1141类似:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 const int MAXN = 107;
     7 int dp[MAXN][MAXN];
     8 char s[MAXN];
     9 
    10 bool Judge(int i, int j)
    11 {
    12     if (s[i] == '('&&s[j] == ')') return true;
    13     if (s[i] == '['&&s[j] == ']') return true;
    14     return false;
    15 }
    16 
    17 void Print(int l, int r)
    18 {
    19     if (l > r) return;
    20     if (l == r) {
    21         if (s[l] == '(' || s[r] == ')')
    22             printf("()");
    23         else printf("[]");
    24         return;
    25     }
    26     else if (Judge(l, r) && dp[l][r] == dp[l + 1][r - 1]) {
    27         printf("%c", s[l]);
    28         Print(l + 1, r - 1);
    29         printf("%c", s[r]);
    30     }else for(int k=l;k<r;k++)
    31         if (dp[l][r] == dp[l][k] + dp[k + 1][r]) {
    32             Print(l, k);
    33             Print(k + 1, r);
    34             break;
    35         }
    36 }
    37 
    38 int main()
    39 {
    40     while (gets(s))
    41     {
    42         int len = strlen(s);
    43         memset(dp, 0, sizeof(dp));
    44         for (int i = 0; i < len; i++) {
    45             dp[i][i - 1] = 0, dp[i][i] = 1;
    46         }
    47         for (int p = 1; p < len; p++)
    48         {
    49             for (int i = 0; i < len - p; i++) {
    50                 int j = i + p;
    51                 dp[i][j] = 1e9;
    52                 if ((s[i] == '('&&s[j] == ')') || (s[i] == '['&&s[j] == ']'))
    53                     dp[i][j] = dp[i + 1][j - 1];
    54                 for (int k = i; k < j; k++)
    55                     dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
    56             }
    57         }
    58         Print(0, len - 1);
    59         printf("
    ");
    60     }
    61     return 0;
    62 }
    自底向上递推poj1141

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

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 const int MAXN = 107;
     7 int dp[MAXN][MAXN],flag[MAXN][MAXN];
     8 char s[MAXN];
     9 
    10 bool Judge(int i, int j)
    11 {
    12     if (s[i] == '('&&s[j] == ')') return 1;
    13     if (s[i] == '['&&s[j] == ']') return 1;
    14     return 0;
    15 }
    16 
    17 int dfs(int i, int j)
    18 {
    19     if (dp[i][j] != -1) return dp[i][j];
    20     if (i > j) return 0;
    21     if (i == j) return 1;
    22     int ans = 1e9;
    23     if (Judge(i,j)) {
    24         ans = min(ans, dfs(i + 1, j - 1));
    25         flag[i][j] = -1;
    26     }
    27     for (int k = i; k < j; k++) {
    28         if (ans > dfs(i, k) + dfs(k + 1, j)) {
    29             ans = dfs(i, k) + dfs(k + 1, j);
    30             flag[i][j] = k;
    31         }
    32     }
    33     return dp[i][j] = ans;
    34 }
    35 
    36 void Print(int i, int j)
    37 {
    38     if (i>j) return;
    39     if (i == j) {
    40         if (s[i] == '(' || s[j] == ')') printf("()");
    41         else printf("[]");
    42         return;
    43     }
    44     else if (flag[i][j] == -1) {
    45         printf("%c", s[i]);
    46         Print(i + 1, j - 1);
    47         printf("%c", s[j]);
    48         return;
    49     }
    50     else {
    51         Print(i, flag[i][j]);
    52         Print(flag[i][j] + 1, j);
    53     }
    54 }
    55 
    56 int main()
    57 {
    58     int T;
    59     scanf("%d", &T);
    60     getchar();
    61     while (T--)
    62     {
    63         gets(s);
    64         gets(s);
    65         int len = strlen(s);
    66         memset(dp, -1, sizeof(dp));
    67         dfs(0, len - 1);
    68         Print(0, len - 1);
    69         printf("
    ");
    70         if (T)
    71             printf("
    ");
    72     }
    73     return 0;
    74 }
    记忆化搜索Uva 1626

    poj 1141的完全类似。。。

  • 相关阅读:
    教程:在 Visual Studio 中开始使用 Flask Web 框架
    教程:Visual Studio 中的 Django Web 框架入门
    vs2017下发现解决python运行出现‘No module named "XXX""的解决办法
    《sqlite权威指南》读书笔记 (一)
    SQL Server手工插入标识列
    hdu 3729 I'm Telling the Truth 二分图匹配
    HDU 3065 AC自动机 裸题
    hdu 3720 Arranging Your Team 枚举
    virtualbox 虚拟3台虚拟机搭建hadoop集群
    sqlserver 数据行统计,秒查语句
  • 原文地址:https://www.cnblogs.com/zxhyxiao/p/7419105.html
Copyright © 2011-2022 走看看