输入括号
输入:(注意:据说有空行,用getline()或者gets()输入)
([(]
([(((])))
[]]]]]
)))(((
要求输出最小要加入的括号数目之后的匹配括号串
分析:
经典dp题,如果用dp自底向上的递推做的话,比较麻烦,其实用记忆化做很简单,
每次递归前加上判断是否已经计算过即可减少计算量,相当于dfs里的剪枝,代码如下
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
#define X 205
#define INF 1000
int dp[X][X],pre[X][X];
string s;
int f(int i,int j) //记忆化
{
if(dp[i][j]!=-1) //当计算过时
return dp[i][j];
if(i>j) //当前面的指针大于后面的指针时,已经结束这次递归
return 0;
if(i==j) //恰巧相等时,加一
return 1;
int ans = INF;
if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))
{
ans = f(i+1,j-1); //当两者恰好能组成一对括号时
pre[i][j] = -1;
}
for(int k=i;k<j;k++) //相当于最短路时的松弛操作
{
if(ans>f(i,k)+f(k+1,j))
{
ans = f(i,k)+f(k+1,j);
pre[i][j] = k; //记录中间断开的点,递归打印括号
}
}
dp[i][j] = ans; //用辅助数组储存状态,思想为用空间换取时间
return ans;
}
void print(int i,int j)
{
if(i>j)
return;
if(i==j) //但两者相同位置时,直接输出该对括号
{
if(s[i]=='('||s[i]==')')
printf("()");
else if(s[i]=='['||s[j]==']')
printf("[]");
}
else if(pre[i][j]==-1)
{ //当后面已有它匹配的串时,先输出该括号,再递归打印出中间的括号,再打印后面匹配的括号
cout<<s[i];
print(i+1,j-1);
cout<<s[j];
}
else //递归打印以k为分界的的括号
{
print(i,pre[i][j]);
print(pre[i][j]+1,j);
}
}
int main()
{
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
while(getline(cin,s))
{
memset(pre,-1,sizeof(pre));
memset(dp,-1,sizeof(dp));
if(s.size()) //注意到有空行
{
f(0,s.size()-1);
print(0,s.size()-1);
cout<<endl;
}
else
cout<<endl;
}
return 0;
}