和前面一样,要注意状态的枚举顺序,边界是d[i+1][i] = 0 和d[i][i] = 1 ,所以枚举的区间应该从小到大,大区间依赖于小区间的最优解 。
然后就是状态的转移,是如何转移的呢? d[i][j]表示字符串i~j的最优解,那么先检查i与j是否匹配,如果匹配,状态转移可以转移到d[i+1][j-1] 。 无论是否匹配,状态还都能转移到子区间上:d[i][k] 和 d[k+1][j] ,这是不是就像最优矩阵链乘问题了? 只不过该题对正规括号序列的定义有一条:如果S是正规括号序列,那么(S)和[S]也都是正规括号序列,所以才有了那个多加的一条dp 。
该题的难点还在于解的打印和输入输出的格式控制(空序列陷阱) 。 细节参见代码:
#include<bits/stdc++.h> using namespace std; const int maxn = 109; int T,n,d[maxn][maxn]; string s; bool match(char a,char b) { if((a=='('&&b==')')||(a=='['&&b==']')) return true; else return false; } void print(int i,int j) { if(i > j) return ; if(i == j) { if(s[i] == '(' || s[i] == ')') printf("()"); //非正规序列,补充成正规序列 else printf("[]"); return ; } int ans = d[i][j] ; if(match(s[i],s[j]) && ans == d[i+1][j-1]) { //ans == d[i+1][j-1] 这个条件极其重要,只有满足这点,才能说明其依赖的子最优解是什么 printf("%c",s[i]); print(i+1,j-1) ; printf("%c",s[j]); return ; } for(int k=i;k<j;k++) { if(ans == d[i][k] + d[k+1][j]) {//逐步回调寻找,寻找最优解所依赖的子最优解 print(i,k); print(k+1,j); return ; } } } int main() { scanf("%d",&T); int c = getchar(); c = getchar(); while(T--) { getline(cin,s); n = s.size(); if(n == 0) ; else { for(int i=0;i<n;i++) { d[i][i] = 1; d[i+1][i] = 0; } for(int i=n-2;i>=0;i--) { for(int j=i+1;j<n;j++) { d[i][j] = n; if(match(s[i],s[j])) d[i][j] = min(d[i][j],d[i+1][j-1]); for(int k=i;k<j;k++) d[i][j] = min(d[i][j],d[i][k]+d[k+1][j]); } } print(0,n-1); } printf("\n"); //格式控制 if(T) printf("\n"); if(T) c = getchar(); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-05 12:41:01