一. Vjudge链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=51187
二. 题目大意:给出一个只含()[]的序列,问添加多少个字符能让其变为regular brackets sequences,就是每个括号,都能找到一个与其相对应的另一半。要求输出添加之后的结果,注意有空串。
三. 思路:区间DP问题,关于区间DP请看 http://blog.youkuaiyun.com/h992109898/article/details/51469784
1. 直接dp[i][j]表示i~j的最优值,注意要先拓展小区间,因此枚举终点,离终点最近的一个点开始拓展。
状态转移:
if(match(i, j)
dp[i][j] = dp[i+1][j-1];
dp[i][j] = min(dp[i][k] + dp[k+1][j])
k表示 i <= k < j。
因此我们要初始化dp[i][i] = 1,表示每个字符如果只有它自己的话还需要另一半。
dp[i+1][i] = 0。在2个字符相邻并且匹配的时候,转换成更加小的区间dp[i+1][i],那么此时它们的值会是0。
2. 此题要求的是打印出一个解,而其实在DP的过程中,我们只要记录一下路径,makePair[i][j]首先全部置为-1。-2表示 (i,j) 匹配,里面的值若为正数k表示在 (i,j) 这个区间内,在k后面一个位置放一个与i匹配的,和在k+1前面一个位置放一个与j匹配的情况最优,然后递归打印解,记住可能出现i>j的情况,直接剪枝。
3. 最后吐槽一下输入输出,有空串不能用scanf啊,输入输出格式要仔细看啊,不然WA和TLE都不知道怎么回事啊。
四. 代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N = 128,
INF = 0x3f3f3f3f;
int dp[MAX_N][MAX_N], makePair[MAX_N][MAX_N];
char str[MAX_N];
bool match(int i, int j)
{
return str[i] == '(' && str[j] == ')'
||
str[i] == '[' && str[j] == ']';
}
void print(int i, int j)
{
if(i > j)
return;
if(i == j){
if(str[i] == '(' || str[i] == ')')
printf("()");
else
printf("[]");
return;
}
if(makePair[i][j] == -2){
printf("%c", str[i]);
print(i+1, j-1);
printf("%c", str[j]);
return;
}
int k = makePair[i][j];
print(i, k);
print(k+1, j);
}
int main()
{
//freopen("in.txt", "r", stdin);
int i, j, test, len, k;
scanf("%d", &test);
getchar();
while(test--){
getchar();
memset(str, 0, sizeof(str));
memset(makePair, -1, sizeof(makePair));
gets(str);
len = strlen(str);
for(i = 0; i < len; i++)
dp[i][i] = 1, dp[i+1][i] = 0;
for(j = 1; j < len; j++){
for(i = j-1; i >= 0; i--){
dp[i][j] = INF;
if(match(i, j)){
dp[i][j] = dp[i+1][j-1];
makePair[i][j] = -2;
}
for(k = i; k < j; k++)
if(dp[i][j] > dp[i][k]+dp[k+1][j]){
dp[i][j] = dp[i][k]+dp[k+1][j];
makePair[i][j] = k;
}
}
}
print(0, len-1);
printf("\n");
if(test){
printf("\n");
}
}
return 0;
}