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的完全类似。。。

  • 相关阅读:
    jquery 点击图片弹出遮罩层查看大图
    js 禁止复制粘贴
    如何设置ASP.NET页面的运行超时时间
    Asp.net中DataBinder.Eval用法的总结
    C#中DateTime的缺陷与代替品DateTimeOffset的对吧
    itextsharp html转成pdf 特殊符号异常处理
    C#中DateTime和DateTimeOffset的对比
    最新版本sublime text3注册码
    System.Web.Optimization找不到引用
    nodejs npm常用命令
  • 原文地址:https://www.cnblogs.com/zxhyxiao/p/7419105.html
Copyright © 2011-2022 走看看