括号配对(区间dp,记忆化搜索实现)

题目链接
事实证明,一道题如果你第一次做错了,那么第二次做的时候很可能依旧会错.
当初写这道题就被细节折磨了很久,现在第二次做还是被折磨了.
思路分析:记忆化搜索嘛,就是写主框架+抠递归边界.依旧是自顶向下的方法写.
主框架:
最终状态是一整条字符串需要的最小字符数.可以这么去考虑问题,如果说字符串的头尾是匹配的.那么我们可以消掉一组符号,然后求取[i+1,j-1]范围所需要的字符数.不过要注意的是.[i+1,j-1]范围的字符串需要的个数不一定就比[i,j]这个原先范围的字符串需要的个数少(这也是我第一次做的时候没遇到但是第二次做的时候遇到的问题).给个最简单样例那就是:()()这个样例.如果认为[i+1,j-1]范围上就一定比较少的话,那么得出的答案应该是2.但其实不消掉头尾的字符组合能得出更小的答案.最好的划分应该是[1,2]+[3,4] = 0.一开始也是考虑少了,以为范围变小了答案肯定会小.事实却不是这样的…划分区间就没啥难度了.常规的[i,k][k+1,j].
边界:
在第一次做的时候是比较困扰我的地方,不过现在用记忆化搜索的时候就比较容易考虑边界了(这也是我为什么更喜欢记忆化搜索来写某些dp的原因).
我们考虑分割到最小的子问题是怎样的.其实就是单个的像’[’,’)'这样的字符.这个边界所需要的匹配数就是1.
因为我们用了i+1,j-1.这个范围是有可能导致i>j的.而基于第一个边界.可以知道如果出现i>j就一定是由()或者[]转移过来的.那么这个边界的匹配数应该是0,因为已经是一个匹配的字符了.
边界问题就处理完了.只要加几行代码到主框架里面就可以了.
两个步骤弄清楚.代码也就好写了.
附上代码

#define LL long long
#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
#include <cmath>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 110;
char a[N];
int dp[N][N];
bool judge(char a,char b){
	if(a == '[' && b == ']') return true;
	if(a == '(' && b == ')') return true;
	return false;
}
int dfs(int i,int j){
	if(dp[i][j] != INF) return dp[i][j];
	if(i > j) return dp[i][j] = 0;
	if(i == j) return dp[i][j] = 1;
	if(judge(a[i],a[j])) dp[i][j] = dfs(i+1,j-1);
	for(int k=i;k<j;++k) dp[i][j] = min(dp[i][j],dfs(i,k)+dfs(k+1,j));
	return dp[i][j];
}
int main(){
	scanf("%s",a+1);
	int n = strlen(a+1);
	memset(dp,0x3f,sizeof(dp));
	cout << dfs(1,n) << endl;
	
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值