题意:给定一个串,可能空串,或由'[',']','(',')'组成。问使其平衡所需添加最少的字符数,并打印平衡后的串。
分析:dp[i][j]表示区间(i,j)最少需添加的字符数。
1、递推。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cctype> #include<cmath> #include<iostream> #include<sstream> #include<iterator> #include<algorithm> #include<string> #include<vector> #include<set> #include<map> #include<stack> #include<deque> #include<queue> #include<list> #define lowbit(x) (x & (-x)) const double eps = 1e-8; inline int dcmp(double a, double b){ if(fabs(a - b) < eps) return 0; return a > b ? 1 : -1; } typedef long long LL; typedef unsigned long long ULL; const int INT_INF = 0x3f3f3f3f; const int INT_M_INF = 0x7f7f7f7f; const LL LL_INF = 0x3f3f3f3f3f3f3f3f; const LL LL_M_INF = 0x7f7f7f7f7f7f7f7f; const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1}; const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1}; const int MOD = 1e9 + 7; const double pi = acos(-1.0); const int MAXN = 100 + 10; const int MAXT = 10000 + 10; using namespace std; char s[MAXN]; int dp[MAXN][MAXN]; int len; bool match(char a, char b){//判断是否平衡 return (a == '(' && b == ')') || (a == '[' && b == ']'); } void solve(){ for(int i = 0; i < len; ++i){ dp[i + 1][i] = 0;//l>r,该情况不需添加字符 dp[i][i] = 1;//只有一个字符,无论是什么,都需要一个字符将其补全 } for(int i = len - 2; i >= 0; --i){ for(int j = i + 1; j < len; ++j){ dp[i][j] = len;//len长度的串,最多就需要len个字符使其平衡,此处初始化成个最大值即可 if(match(s[i], s[j])){//如果当前串满足(S)或[S],则转移到S这个情况。 dp[i][j] = min(dp[i][j], dp[i + 1][j - 1]); } for(int k = i; k < j; ++k){//在当前区间里枚举分割线 dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]); } } } } void print(int i, int j){ if(i > j) return; if(i == j){ if(s[i] == '(' || s[i] == ')') printf("()"); else printf("[]"); return; } int ans = dp[i][j]; if(match(s[i], s[j]) && ans == dp[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 == dp[i][k] + dp[k + 1][j]){ print(i, k); print(k + 1, j); return; } } } int main(){ int T; scanf("%d", &T); getchar(); while(T--){ gets(s); gets(s); len = strlen(s); solve(); print(0, len - 1); printf(" "); if(T) printf(" "); } return 0; }
2、记忆化搜索,更好理解些。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cctype> #include<cmath> #include<iostream> #include<sstream> #include<iterator> #include<algorithm> #include<string> #include<vector> #include<set> #include<map> #include<stack> #include<deque> #include<queue> #include<list> #define lowbit(x) (x & (-x)) const double eps = 1e-8; inline int dcmp(double a, double b){ if(fabs(a - b) < eps) return 0; return a > b ? 1 : -1; } typedef long long LL; typedef unsigned long long ULL; const int INT_INF = 0x3f3f3f3f; const int INT_M_INF = 0x7f7f7f7f; const LL LL_INF = 0x3f3f3f3f3f3f3f3f; const LL LL_M_INF = 0x7f7f7f7f7f7f7f7f; const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1}; const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1}; const int MOD = 1e9 + 7; const double pi = acos(-1.0); const int MAXN = 100 + 10; const int MAXT = 10000 + 10; using namespace std; char s[MAXN]; int dp[MAXN][MAXN]; int len; bool match(char a, char b){ return (a == '(' && b == ')') || (a == '[' && b == ']'); } int solve(int i, int j){ if(dp[i][j] != INT_INF) return dp[i][j]; if(i > j) return dp[i][j] = 0; if(i == j) return dp[i][j] = 1; if(match(s[i], s[j])) dp[i][j] = min(dp[i][j], solve(i + 1, j - 1)); for(int k = i; k < j; ++k){//枚举分割线,串AB所需添加的最少字符数可转移到串A所需添加的最少字符数+串B所需添加的最少字符数 dp[i][j] = min(dp[i][j], solve(i, k) + solve(k + 1, j)); } return dp[i][j]; } void print(int i, int j){ if(i > j) return; if(i == j){ if(s[i] == '(' || s[i] == ')') printf("()"); else printf("[]"); return; } int ans = dp[i][j]; if(match(s[i], s[j]) && ans == dp[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 == dp[i][k] + dp[k + 1][j]){ print(i, k); print(k + 1, j); return; } } } int main(){ int T; scanf("%d", &T); getchar(); while(T--){ memset(dp, INT_INF, sizeof dp); gets(s); gets(s); len = strlen(s); solve(0, len - 1); print(0, len - 1); printf(" "); if(T) printf(" "); } return 0; }