跟括号匹配(一)截然不同 二 不在考栈的应用了 这道题是一道区间dp
这道题写了很久 自己没写出来 参考了网上超过15篇博客才算是略知一二
本文提供两种解法
方法一:
定义dp[i][j] 为从位置I到位置j所需要的括号数量 状态转移的过程是这样的 枚举I和j 或者长度 如果第I个点和第j个点匹配 那么 dp[i][j] 是不是应该等于min(dp[I][j], dp[I + 1][j - 1]) 呢
如果不匹配那么我们可以枚举I到j中间所有的点 尝试是否能松弛 其实是尝试能否减小dp[I][j] 类似于松弛的操作 对就是这样 值得注意的是边界问题 和 dp数组的初始化问题
单独的一个位置dp[i][I]应该初始化为多少呢 没错 初始化为1 需要一个额外的括号使之匹配 差不多这样
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 105;
const int inf = 0x3f3f3f3f;
int main()
{
int t, dp[maxn][maxn];
char str[maxn];
cin >> t ;
while ( t -- ) {
cin >> str ;
int length = strlen(str);
for (int i = 0; i < length; i ++) {
for (int j = 0; j < length ; j ++) {
if(i < j) dp[i][j] = inf;
else if (i == j)dp[i][j] = 1;
else dp[i][j] = 0;
}
}//len 不从0开始是因为枚举长度为0没有意义
for (int len = 1; len < length; len ++) {//len表示从i开始的一段长度为len的区间
for (int i = 0; i < length - len; i ++) {//i表示区间的起点
int j = len + i;//j表示区间的终止点
if(str[i] == '(' && str[j] == ')' || str[i] == '[' && str[j] == ']')
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]);
}
}
cout << dp[0][length - 1] << endl;
}
}
方法二 :如果我们能找到这一串括号中的最大匹配量 那么需要额外添加的括号就是长度减去最大匹配量
稍微更改下方法一的代码就能得到方法二的代码 建议先看懂方法一在看方法二
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 105;
const int inf = 0x3f3f3f3f;
int main()
{
int t, dp[maxn][maxn];
char str[maxn];
cin >> t ;
while ( t -- ) {
cin >> str ;
int length = strlen(str);
memset(dp , 0, sizeof(dp));
for (int len = 1; len < length ; len ++) {//len依旧枚举长度
for (int i = 0; i < length - len; i ++) {//i为起点
int j = len + i;//终点
if(str[i] == '(' && str[j] == ')' || str[i] == '[' && str[j] == ']')
dp[i][j] = dp[i + 1][j - 1] + 2;//如果匹配 那么当前区间的值肯定是一个较小的区间的值+2来的
for (int k = i; k < j; k ++)//松弛过程
dp[i][j] = max(dp[i][j] , dp[i][k] + dp[k + 1][j]);
}
}
cout << length - dp[0][length - 1] << endl;
}
}