括号序列(区间dp)

括号序列(区间dp)

输入一个长度不超过100的,由"(",")","[",")"组成的序列,请添加尽量少的括号,得到一个规则的括号序列。如有多解,输出任意一个序列即可。

括号序列是这样定义而成的:

  • 空序列是括号序列
  • 如果S是括号序列,那么(S)和[S]也是正规括号序列
  • 如果A和B都是正规括号序列,那么AB也是正规括号序列。

所以,只要一个括号序列不是空序列,我们一定可以把它从两端剥开,或者把它划分成两个小括号序列。设\(f[i][j]\)表示字串\(s[i][j]\)至少要添加几个括号,那么\(f[i][j]\)一定可以转移到\(f[i][k]+f[k][j]\)。如果\(s[i]=s[j]\),那么也可以转移到\(f[i+1][j-1]\)

由此可见,区间dp的必要条件是划分后解会不同。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn=105;
int T, f[maxn][maxn], n;
char s[maxn];

//函数能使语义更清晰
bool match(int x, int y){ return s[y]-s[x]>0&&s[y]-s[x]<=2; }

void print(int l, int r){  //[l,r]
    if (l>r) return;
    if (l==r){
        if (s[l]=='('||s[l]==')') printf("()");
        else printf("[]"); return; }
    int ans=f[l][r];
    if (match(l, r)&&f[l+1][r-1]==ans){
        putchar(s[l]); print(l+1, r-1); putchar(s[r]);
        return; }
    for (int k=l; k<r; ++k)
        if (f[l][k]+f[k+1][r]==ans){
            print(l, k); print(k+1, r); return; }
}

int main(){
    scanf("%d", &T);
    while (T--){
        fgets(s, maxn, stdin);
        fgets(s, maxn, stdin);
        n=strlen(s)-1;
        for (int i=0; i<n; ++i) f[i+1][i]=0, f[i][i]=1;
        for (int i=n-2; i>=0; --i)
            for (int j=i+1; j<n; ++j){
                f[i][j]=n;
                if (match(i, j)) f[i][j]=f[i+1][j-1]; //[i,j]
                for (int k=i; k<j; ++k)
                    f[i][j]=min(f[i][j], f[i][k]+f[k+1][j]);
            }
        print(0, n-1); puts("");
    }
    return 0;  //忘记return 0了!!
}
posted @ 2018-04-17 11:38  pechpo  阅读(442)  评论(0编辑  收藏  举报