括号匹配(POJ2955)题解

原题地址:http://poj.org/problem?id=2955

题目大意:给出一串括号,求其中的最大匹配数。

我这么一说题目大意估计很多人就蒙了,其实我看到最开始的时候也是很蒙的。这里就来解释一下题意。

这道题让求的是最大常规匹配数,什么是常规匹配呢?

()  []  ()[]  []()  [()]都是常规序列。

翻译一下题目的英文:

我们给出了“常规括号”序列的以下归纳定义:

  • 空序列是常规括号序列,
  • 如果s是常规括号序列,则(s)和[ s ]是常规括号序列,并且
  • 如果ab是常规括号序列,则ab是常规括号序列。
  • 没有其他序列是常规括号序列

样例输入

((()))
()()()
([]])
)[)(
([][][)
end

样例输出

6
6
4
0
6

现在开始说思路。
很明显,这是一道区间DP,因为大的常规括号一定是包含小的常规括号的,满足我们区间DP的要求。
区间DP常用枚举方式见
https://www.cnblogs.com/lizitong/p/10014809.html
首先是状态,非常简单,dp[i][j]表示从i到j的最大匹配数,我们要求的答案就是dp[1][n]
我们开始枚举,假如我们枚举到的[i][j]中,第i个和第j个是匹配的括号,即()或者[ ]
那么dp[i][j]=dp[i+1][j-1]+2;
没错吧,我们只需要在原基础上+2就好了。
如果[i] 和[j]不匹配呢?
那我们可以把这个区间分为两段,以k为分界点。
分成[i][k],[k+1][j]两段,然后把两段的值相加就好了。因为这两个括号不匹配,不代表其中的不匹配。人不能在一棵树上吊死。
需要注意的是,即使是[i]和[j]两个括号匹配,仍需枚举k来判断是否可以将这个区间分成两段,因为这样得到的仍然可能不是最优解。
例如:()()这个序列。
显然答案应该为4,但是最左面和最右面匹配,而中间 ")("
区间的答案为0,导致最后的结果为2。
枚举k的时候需要注意枚举范围。
上代码。
提示:个人区间是从0存到的n-1。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
char a[105];
int dp[105][105];
int main()
{
      while(scanf("%s",a)!=EOF)
      {       
            if(a[0]=='e')
            {
                  return 0;
            }
            int n = strlen(a);
            memset(dp,0,sizeof(dp));
            for(int len = 2;len<=n;len++)
            {
                  for(int i = 0;i+len-1<=n-1;i++)
                  {
                        int j = i+len-1;
                        if((a[i]=='('&&a[j]==')')||(a[i]=='['&&a[j]==']'))
                        {
                              dp[i][j] = max(dp[i][j],dp[i+1][j-1]+2);
                        }
                        
                        for(int k = i;k<=j;k++)
                        {
                               dp[i][j] = max(dp[i][j],dp[i][k]+dp[k+1][j]);
                        }    
                  }   
            }
            printf("%d\n",dp[0][n-1]);
      }
      return 0;
}


 
原文地址:https://www.cnblogs.com/lizitong/p/10020674.html