首先我们看第一题 是很简单的区间dp 问一个符号串 最大的完美匹配数是多少
表示区间l到r最大的完美匹配数 根据区间dp的性质 是由较小的区间向较大的区间转移
根据题意一开始的元区间 即一个符号不构成完美匹配
当l和r构成一对匹配的时候 显然有
或者的最大值 是由某两个子区间合并而来 这一部分类似区间dp的套路
暴力枚举断点 取最大值即可 具体看代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s[120];
int dp[120][120];
int main(){
while(~scanf("%s",s+1)){
if(s[1]=='e') break;
int len = strlen(s+1);
memset(dp,0,sizeof(dp));
for(int i = 2; i <= len; i++){
for(int l = 1; l <= len-i+1; l++){
int r = l+i-1;
if(s[l]=='('&&s[r]==')'||s[l]=='['&&s[r]==']')
dp[l][r]=dp[l+1][r-1]+2;
for(int k = l; k < r; k++)
dp[l][r]=max(dp[l][r],dp[l][k]+dp[k+1][r]);
}
}
printf("%d\n",dp[1][len]);
}
return 0;
}
接下来我们来看第二题 问一个符号串要补最少多少个符号才能全部完美匹配 并输出补完后的符号串
首先 最少要补多少个 其实就是n减去最多的完美匹配数 但这题不是简单的求数量 我们还得知道哪些地方需要补上符号
众所周知 dp常用的输出过程的方法是开个一样大小的数组 在状态转移的时候 记录路径 最后递归输出
所以我开了一个res数组来记录
我们先想一下最小需要补的符号数 和上面很类似
元区间 其他的初始化为0
显然如果l和r完全匹配的话 答案不会增加 令
接下来我们要枚举断点去找最小值 转移成功的话 记录转移的断点 如果上一步令res[l][r]=-1的话 到这里也有可能会覆盖 是不是-1对输出的方式会有影响 具体看代码
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e3+10;
int dp[N][N],res[N][N];
char s[N];
const int inf = 1e9;
void output(int l,int r){
if(l>r) return;
if(l==r){
if(s[l]=='['||s[l]==']') printf("[]");
else printf("()");
return;
}
if(res[l][r]==-1){
putchar(s[l]);
output(l+1,r-1);
putchar(s[r]);
}else{
output(l,res[l][r]);
output(res[l][r]+1,r);
}
}
int main(){
while(gets(s+1)){
int len = strlen(s+1);
memset(dp,0,sizeof(dp));
for(int i = 1; i <= len; i++) dp[i][i]=1;
for(int i = 2; i <= len; i++){
for(int l = 1; l <= len-i+1; l++){
int r = l+i-1;
dp[l][r]=inf;
if(s[l]=='['&&s[r]==']'||s[l]=='('&&s[r]==')')
dp[l][r]=dp[l+1][r-1],res[l][r]=-1;
for(int k = l; k < r; k++)
if(dp[l][r]>dp[l][k]+dp[k+1][r])
dp[l][r]=dp[l][k]+dp[k+1][r],res[l][r]=k;
}
}
output(1,len);
puts("");
}
return 0;
}