题意简述
给定一个括号序列(长度 < = 100 <=100 <=100),但是这个括号序列不一定匹配,请用最少的添加配好这个序列。输出配好的序列。
数据
输入
(
[
(
]
([(]
([(]
输出
(
)
[
(
)
]
()[()]
()[()]
思路
这个。。。第一眼看到,栈?
仿佛不能做。。。但是这个题好像珂以区间
D
P
DP
DP?
d
p
[
l
]
[
r
]
dp[l][r]
dp[l][r]表示从
l
l
l到
r
r
r的最优解,
p
o
s
[
l
]
[
r
]
pos[l][r]
pos[l][r]表示
l
l
l到
r
r
r取最优解时的断点(如果
=
−
1
=-1
=−1就表示不需要断点)。
显然有边界
d
p
[
i
]
[
i
]
=
1
dp[i][i]=1
dp[i][i]=1,因为长度为
1
1
1的字符串无论如何都需要补一个使得能匹配。
考虑转移。
如果
s
[
l
]
s[l]
s[l]和
s
[
r
]
s[r]
s[r]能匹配,显然有
d
p
[
l
]
[
r
]
=
d
p
[
l
+
1
]
[
r
−
1
]
dp[l][r]=dp[l+1][r-1]
dp[l][r]=dp[l+1][r−1],断点什么的就交给中间(即
l
+
1
l+1
l+1到
r
−
1
r-1
r−1)记录去吧,反正
l
l
l到
r
r
r是不用记录的,直接
p
o
s
=
−
1
pos=-1
pos=−1处理。
如果不能匹配,就要考虑别的情况了。显然,
d
p
[
l
]
[
r
]
=
m
i
n
{
d
p
[
l
]
[
k
]
+
d
p
[
k
+
1
]
[
j
]
}
dp[l][r]=min\{dp[l][k]+dp[k+1][j]\}
dp[l][r]=min{dp[l][k]+dp[k+1][j]},其中
l
<
=
k
<
r
l<=k<r
l<=k<r。一边枚举
k
k
k,一遍记录最优断点,存到
p
o
s
pos
pos里面。
现在考虑如何输出解。
和 2003NOIP-TG加分二叉树 那个题类似,我们定义
D
F
S
(
l
,
r
)
DFS(l,r)
DFS(l,r)为输出
l
l
l到
r
r
r的解的函数。
如果
l
>
r
l>r
l>r,直接返回(不合FA♂)
如果
l
=
=
r
l==r
l==r,那么一定要补上一个的。如果
s
[
l
]
=
=
(
或
)
s[l]==(或)
s[l]==(或),那么输出
(
)
()
()。否则输出
[
]
[]
[]。
如果
p
o
s
[
l
]
[
r
]
=
−
1
pos[l][r]=-1
pos[l][r]=−1,记得是什么情况么?我们只要
D
F
S
(
l
+
1
,
r
−
1
)
DFS(l+1,r-1)
DFS(l+1,r−1),然后两边套上括号即可。当然,套那种括号就不用判了,根据
p
o
s
[
l
]
[
r
]
=
−
1
pos[l][r]=-1
pos[l][r]=−1的神奇性质,我们只要在两边套上
s
[
l
]
s[l]
s[l],
s
[
r
]
s[r]
s[r]就是那一对括号。
否则,十分简单,分两块即可。
D
F
S
(
l
,
p
o
s
[
l
]
[
r
]
)
DFS(l,pos[l][r])
DFS(l,pos[l][r]),然后
D
F
S
(
p
o
s
[
l
]
[
r
]
+
1
,
r
)
DFS(pos[l][r]+1,r)
DFS(pos[l][r]+1,r)即可。
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 110
using namespace std;
namespace Flandle_Scarlet
{
char s[N];int n;
int dp[N][N];
int pos[N][N];
bool check(char x,char y)//判断x和y是否是匹配的括号
{
if (x=='(' and y==')') return 1;
if (x=='[' and y==']') return 1;
return 0;
}
void DP()
{
memset(dp,0,sizeof(dp));
for(int i=0;i<n;++i)
{
dp[i][i]=1;//边界条件
}
for(int len=1;len<n;++len)//枚举长度
{
for(int l=0;l+len<n;++l)//枚举左端点
{
int r=l+len;//计算右端点
dp[l][r]=0x7fffffff;//初始化很大
if (check(s[l],s[r]))//如果s[l]和s[r]能匹配
{
if (dp[l+1][r-1]<dp[l][r])//就要考虑用dp[l+1][r-1]更新dp[l][r]
{
dp[l][r]=dp[l+1][r-1];
pos[l][r]=-1;
}
}
for(int k=l;k<r;++k)//枚举断点k
{
if (dp[l][r]>dp[l][k]+dp[k+1][r])
{
dp[l][r]=dp[l][k]+dp[k+1][r];
pos[l][r]=k;//更新答案
}
}
}
}
}
void DFS(int l,int r)//都解释过,此处注释略
{
if (l>r) return;
if (l==r)
{
if (s[l]=='(' or s[r]==')')
{
printf("()");
}
else
{
printf("[]");
}
return;
}
else if (pos[l][r]==-1)
{
putchar(s[l]);
DFS(l+1,r-1);
putchar(s[r]);
}
else
{
DFS(l,pos[l][r]);
DFS(pos[l][r]+1,r);
}
}
void Main()
{
while(gets(s)!=NULL)//听说不gets会炸(白费我10次提交)
{
n=strlen(s);
DP();
DFS(0,n-1);
putchar('\n');//结构清晰
}
}
}
int main()
{
Flandle_Scarlet::Main();
return 0;
}