点击链接哦:https://vjudge.net/problem/51187/origin
题目大意:括弧的序列,在一个字符串中只包含" ( " " ) "和“ [ ” “ ] ”,要求空序列为正确的括弧,如果s是正确的序列,那么[s]和(s)也是正确的序列,如果a和b是是正确的序列,那么ab也是正确的序列。
求在给出的字符串基础上要至少添加多少括号才能使得字符序列是正确的。
ps:紫书p278
一开始自己先是用模拟去做的,自己感觉应该能可以模拟出来,而且长度为100也不会超时。但还是考虑的情况太少了,最终再无数的debug中把这个方法给pass掉,还是看了一下紫书的讲解。对于菜鸟的我在短时间内把这个题想到用dp,而且解决确实有点难度。看到转移方程时感觉就是通过题意把情况给遍历一遍一样。但是自己却没想到。
既然是区间dp,那么肯定是二维的了 设dp[i][j]表示至少需要增加的括号个数。
那么在区间i~j上,如果s[i]和s[j]匹配的话,那么最少增加的括号个数就是区间i+1~j-1上的了,那么如果不匹配的话,那就应该是有两个正确的序列组合而成,如果在区间i~j上有一个k,正确的序列是由dp[i][k]+dp[k+1][j]而来,(其实这只是构成正确序列的一种办法,还有其他的办法也能构成最少的正确的序列)。边界:当序列为空时dp[][]=0;当序列只有一个字符是,那么肯定要补上一个,显然dp[i][i]=1;然后用递推求出。在这里有一个关键,就是当s[i]和s[j]匹配的话,还需要通过第二种方案更新吗?如果不同动脑子的话我想为了保险需要比较一下看看能不能更优化。然而书上也是强调必须要在第二种方案上在比较更新一下。比如有序列" [ ] [ ] ",显然左右是匹配的,那么会得到序列“ ] [ ”,显然要多加两个括号,不合情理。
另一个重点就是打印,仍然是递归打印,具体看代码吧;
坑点:注意输入and输出
///典型的区间DP
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
string s;
int dp[111][111];
int match(char a,char b)
{
if(a=='('&&b==')')return 1;
if(a=='['&&b==']')return 1;
return 0;
}
void print(int i,int j)
{
if(i>j)return ;
if(i==j)
{
if(s[i]=='('||s[j]==')')printf("()");
else printf("[]");
return ;
}
int ans=dp[i][j];
if(match(s[i],s[j])&&ans==dp[i+1][j-1])
{
printf("%c",s[i]);
print(i+1,j-1);
printf("%c",s[j]);
return ;
}
for(int k=i;k<j;k++)
{
if(ans==dp[i][k]+dp[k+1][j])
{
print(i,k);
print(k+1,j);
return ;
}
}
}
int main()
{
int t;
scanf("%d",&t);
getchar();
while(t--)
{
getchar();
getline(cin,s);
int n=s.size();
for(int i=0;i<n;i++)
{
dp[i+1][i]=0;
dp[i][i]=1;
}
for(int i=n-2;i>=0;i--)
{
for(int j=i+1;j<n;j++)
{
dp[i][j]=n;
if(match(s[i],s[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]);
}
}
}
print(0,n-1);
printf("\n");
if(t)printf("\n");
}
return 0;
}