首先补充点关于区间dp的知识
区间DP是一类在区间上进行动态规划的最优问题,一般是根据问题设出一个表示状态的dp,可以是二维的也可以是三维的,一般情况下为二维。然后将问题划分成两个子问题,也就是一段区间分成左右两个区间,然后将左右两个区间合并到整个区间,或者说局部最优解合并为全局最优解,然后得解。区间dp专辑
题意:给定一个字符串,问最长匹配的括号长度是多少?
dp[i][j]代表第i个括号到第j个括号的最大匹配长度
整体套路和最优矩阵链乘一样
我觉得本题分区间的一个很关键的特性在于if a and b are regular, ab is regular 需要找到一个分割点,使得左侧和右侧都达到最优状态
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110;
int dp[N][N];
int main()
{
string s;
while(cin>>s)
{
if(s[0]=='e')
break;
memset(dp,0,sizeof(dp));
int len=s.size();
for(int l=2;l<=len;++l)
{
for(int i=0;i+l-1<len;++i)
{
int j=i+l-1;
if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))
dp[i][j]=dp[i+1][j-1]+2;
for(int k=i;k<=j-1;++k) //这个地方写成k=i+1 wa了两发
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
}
}
cout<<dp[0][len-1]<<endl;
}
}
poj 1141 同UVA1626
题意:给定一个由括号构成的字符串,输出由它构成的最小标准括号串。
本题陷阱:输入串可能是空串,因此不能用scanf("%s")的方式输入 (注意!poj上并没有这个坑)
这里dp[i][j]表示第i个括号到第j个括号的最大匹配数(意味最小增加括号数)
其中一种输出方式是用pos[i][j]数组记录从第i个括号到第j个括号从哪里分开,使得左部分和右部分需要添加的括号都最少。
输出的时候就分三种情况,落单的括号(i==j) 补全它
dp[i][j]==dp[i+1][j-1] 时pos=-1,不需要补全括号,先输左括号,输出中间部分,最后输右括号
pos!=-1 分别输出(i,pos),(pos+1,j)
如果i>j要return,不然会停不下来
不是很明白为什么在更新dp[i][j]的时候=号情况不更新会T,是因为迭代也左右区间分的尽量均匀会更好吗?
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=110;
int dp[N][N];
int pos[N][N];
char s[N];
void solve(int i,int j)
{
if(i>j) return ; //没有这一行会runtime error
if(i==j)
{
if(s[i]=='('||s[i]==')') cout<<"()";
else cout<<"[]";
return ;
}
if(pos[i][j]==-1)
{
cout<<s[i];
solve(i+1,j-1);
cout<<s[j];
}
else
{
solve(i,pos[i][j]);
solve(pos[i][j]+1,j);
}
}
int main()
{
// getline(cin,s);
gets(s);
//cout<<s<<endl;
memset(dp,0,sizeof(dp));
int len=strlen(s);
for(int l=2;l<=len;++l)
{
for(int i=0;i+l-1<len;++i)
{
int j=i+l-1;
if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))
{
dp[i][j]=dp[i+1][j-1]+2;
pos[i][j]=-1;
}
for(int k=i;k<=j-1;++k) //这个地方写成k=i+1 wa了两发
{
int tmp=dp[i][k]+dp[k+1][j];
if(tmp>=dp[i][j]) //如果不写=会T???
{
dp[i][j]=dp[i][k]+dp[k+1][j];
pos[i][j]=k;
}
}
}
}
// for(int i=0;i<len;++i)
// {
// for(int j=0;j<len;++j)
// cout<<pos[i][j];
// cout<<endl;
// }
solve(0,len-1);
cout<<endl;
return 0;
}
另外一种输出的方式是在打印的时候重新检查一下哪种决策最好
其对称版本(dp[i][j]表示从第i个括号到第j个括号最少需要增加多少个括号)
//A
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=110;
const int INF=0x3f3f3f3f;
int dp[N][N];
int pos[N][N];
char s[N];
void solve(int i,int j)
{
if(i>j) return ; //没有这一行会runtime error
if(i==j)
{
if(s[i]=='('||s[i]==')') cout<<"()";
else cout<<"[]";
return ;
}
if(pos[i][j]==-1)
{
cout<<s[i];
solve(i+1,j-1);
cout<<s[j];
}
else
{
solve(i,pos[i][j]);
solve(pos[i][j]+1,j);
}
}
int main()
{
// getline(cin,s);
gets(s);
//cout<<s<<endl;
memset(dp,0,sizeof(dp));
int len=strlen(s);
memset(dp,0,sizeof(dp)); //需要添加的括号初始化为0
for(int i=0;i<len;++i)
dp[i][i]=1;
for(int l=2;l<=len;++l)
{
for(int i=0;i+l-1<len;++i) //啊啊啊写成i+len-1了
{
int j=i+l-1;
dp[i][j]=INF; //一开始就初始化为INF是错的
if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))
{
dp[i][j]=dp[i+1][j-1]; //从(i+1,j-1)扩展到(i,j)不需要额外添加括号
pos[i][j]=-1;
}
for(int k=i;k<j;++k)
{
int tmp=dp[i][k]+dp[k+1][j];
if(dp[i][j]>tmp)
{
dp[i][j]=tmp;
pos[i][j]=k;
}
}
}
}
// for(int i=0;i<len;++i)
// {
// for(int j=0;j<len;++j)
// cout<<pos[i][j];
// cout<<endl;
// }
solve(0,len-1);
cout<<endl;
return 0;
}