题意:给出一个括号序列,问最短的补全成合法括号序列是什么。
解法:区间dp。考虑dp[i][j]表示i到j区间补全需要的多余字符个数,则有状态转移方程:dp[i][j] = min{dp[i][k], dp[k + 1][j]},0 <= k < j,if(s[i]s[j] == '()' or '[]') dp[i][j] = min(dp[i][j], dp[i - 1][j - 1])。第一个方程意义为区间i到j的代价是区间i到k加区间k+1到j,枚举k,获得最优解,第二个方程意义为当s[i]和s[j]是一对匹配的括号的时候dp[i][j]就可以等于dp[i -1][j - 1],这两种情况取min。因为要输出的是补全之后的字符串,所以要记录每次的选择,递归回去输出。
PS:不要多组输入!不要多组输入!不要多组输入!别问我怎么知道的……
代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string>
#include<string.h>
#include<math.h>
#include<limits.h>
#include<time.h>
#include<stdlib.h>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#include<iomanip>
#define LL long long
using namespace std;
int ans[105][105];//记录选择,如果是-1说明选择是第二种情况,否则表示第一种情况的k
string s;
void dfs(int l, int r)
{
if(l > r) return ;
if(l == r)
{
if(s[l] == '(' || s[l] == ')')
printf("()");
else printf("[]");
return ;
}
if(ans[l][r] == -1)
{
printf("%c", s[l]);
dfs(l + 1, r - 1);
printf("%c", s[r]);
}
else
{
dfs(l, ans[l][r]);
dfs(ans[l][r] + 1, r);
}
}
int main()
{
cin >> s;
int dp[105][105] = {0};
for(int i = 0; i < 105; i++)
for(int j = i; j < 105; j++)
dp[i][j] = 1000000000;
for(int i = 0; i < s.size(); i++)
dp[i][i] = 1;
for(int j = 0; j < s.size(); j++)
for(int i = j - 1; i >= 0; i--)
{
if(s[i] == '(' && s[j] == ')' || s[i] == '[' && s[j] == ']')//第二种情况
{
dp[i][j] = dp[i + 1][j - 1];
ans[i][j] = -1;
}
for(int 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];
ans[i][j] = k;
}
}
}
dfs(0, s.size() - 1);
puts("");
return 0;
}