这题绝对是经典的括号题,每次碰到括号我总是非常头疼,这次在小白的帮助下终于认真的学习了这题的解法。
这题的算法当时是dp,转移过程比较复杂,用d数组记录当前情况需要添加的括号数,分析d(i,j)的状态,有如下两种:
1.如果s[i]和s[j]配对,那么d(i,j)=d(i+1,j-1);
2.如果字符串有2个及以上的字符,可以将原本的字符串分为两个,转移为d(i,j)=d(i,k)+d(k+1,j);(配对的情况下也要考虑这种情况,因为1的转移未必是最优解,比如[][],如果只用1转移,就是][,要添2个括号,所以要进行2的转移)。
这题最坑爹的莫过于输入输出,第一个数下面要一个空行,每个input之间要一个空行,每个output之间要一个空行。
我学习小白书用的是递推的方法,倒叙枚举i,递推往往是比递归快,我要尽快学会写递推式子。
#include<iostream> #include<cstdio> #include<cctype> #include<cstdlib> #include<cmath> #include<algorithm> #include<cstring> #include<string> #include<vector> #include<queue> #include<map> #include<set> #include<sstream> #include<stack> using namespace std; #define MAX 105 typedef long long LL; const double pi=3.141592653589793; const int INF=1e9; const double inf=1e20; int d[105][105];//记录i-j之间需要添多少个括号。 char s[105]; bool match(char a,char b) { if(a=='('&&b==')') return true; else if(a=='['&&b==']') return true; return false; } void print(int i,int j) { if(i>j) return ; if(i==j) { if(s[i]=='('||s[i]==')') printf("()"); else if(s[i]=='['||s[i]==']') printf("[]"); return ; } int ans=d[i][j]; if(match(s[i],s[j])&&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 ;//这里一定要记得return } } } int main() { int n; scanf("%d",&n); getchar(); while(n--) { int count=0; char c; memset(d,0,sizeof(d));//每个input前有一个空行 memset(s,0,sizeof(s));//初始化s数组不能忘记 gets(s); gets(s); int len=strlen(s);//长度是len,一开始我写错写了n,错了好久。 for(int i=0; i<len; i++) d[i][i]=1; for(int i=len-2; i>=0; i--) { for(int j=i+1; j<len; j++) { d[i][j]=INF; 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,len-1); printf("\n"); if(n) printf("\n");//坑死人不偿命啊,每个output之间还有个空行T_T,论认真读题的重要性 } return 0; }