题目大意
给出一个括号序列,添加最少的括号使序列正确
解题思路
先将问题简单化,从求序列退化为求最小添加括号数的问题
用区间dp n³解决
f[l][r]表示使第l个到r个区间正确的最小添加数
1 :当l = r时, f[l][r] = f[l+1][r-1]
2 : 在l到j中,枚举中间点k,则f[l][r] = min (f[l][r], f[l][k] + f[k+1][r])
求出了最小添加括号数后,再来思考完整的问题
用递归解决输出方案
假设有一个 从 l 到 r 的区间
这个区间的最优解有两种情况:
1:有上述第1种情况求得
2:由上述第2种情况求得
对于 1, 先输出最左边的字符,再递归中间部分,再输出最右边的字符
对于2, 用w数组记录此时最优方案的分割点k,分别递归左半边和右半边
特别情况,当l = r 则需在这个地方添加一个括号与其配对
注意
要对读入序列长度为0进行特判
不然会很惨
几个小时也调不出来qwq
完整代码加注释
(突然发现我的代码好短)
#include <bits/stdc++.h> using namespace std; char ch[105]; int f[105][105], w[105][105]; void out (int x) { if (ch[x] == '(' or ch[x] == ')') cout << "()"; else cout << "[]"; }//输出与单个括号配对的函数 void print (int l, int r) { if (l > r) return; else if (l == r) out (l); //若l = r 则需在这个地方添加一个括号与其配对 else if (w[l][r] == 0) cout << ch[l], print (l + 1, r - 1), cout << ch[r]; //上述情况1 else print (l, w[l][r]), print (w[l][r] + 1, r); //上述情况2 } //输出方案递归函数 int main(){ scanf ("%s", ch + 1); int len = strlen (ch + 1); if (!len) puts(""); //当长度为0的特判 memset (f, 0x3f, sizeof (f)); for (int i = 1; i <= 101; i++) f[i][i] = 1; //长度为1的区间初值赋值为1 for (int l = 2; l <= len; l++) //区间dp for (int i = 1; i <= len - l + 1; i++) { int j = i + l - 1; if ((ch[i] == '[' and ch[j] == ']') or (ch[i] == '(' and ch[j] == ')')) if (l != 2) f[i][j] = f[i+1][j-1]; else f[i][j] = 0; //当这两个可以匹配的情况,注意:当长度为2时要特判 for (int k = i; k < j; k++) if (f[i][j] > f[i][k] + f[k+1][j]) f[i][j] = f[i][k] + f[k+1][j], w[i][j] = k; //枚举并记录下最优方案的分割点 } print (1, len); return 0; }