区间dp经典 括号匹配两题 poj2955 和 poj1141

两个题的链接:括号匹配1 括号匹配2

首先我们看第一题  是很简单的区间dp  问一个符号串  最大的完美匹配数是多少 

dp[l][r]表示区间l到r最大的完美匹配数  根据区间dp的性质  是由较小的区间向较大的区间转移

根据题意一开始的元区间  dp[i][i]=0即一个符号不构成完美匹配 

当l和r构成一对匹配的时候  显然有 dp[l][r]=dp[l+1][r-1]+2

或者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数组来记录

我们先想一下最小需要补的符号数 和上面很类似  

元区间dp[i][i]=1  其他的初始化为0 

显然如果l和r完全匹配的话  答案不会增加 dp[l][r]=dp[l+1][r-1] 令res[l][r]=-1

接下来我们要枚举断点去找最小值  dp[l][r]=dp[l][k]+dp[k+1][r] 转移成功的话 res[i][j]=k 记录转移的断点 如果上一步令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;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值