题目连接:check here~
题意是说给一个字符串,包含‘(‘,‘)‘,‘[‘,‘]‘四种字符,判断至少需要添加几个字符使所给字符串括号匹配。
区间型动态规划,设dp[i][j]表示在字符串s中i位置到j位置所需要添加的最少的字符(i <= j)
有两种情况:
1、dp[i][j] = dp[i+1][j] + 1;
表示:在i到j之间没有与s[i]相匹配的括号,则必须添加一个字符来与之匹配,问题就转化为:从i+1位置到j位置所需要添加的最少的字符+1。
2、dp[i][j] = min{ dp[i+1][k-1] + dp[k+1][j] }; (i < k <= j)
表示:在i到j之间找到一个k使得s[i]与s[k]相匹配,则问题就转化为求:从i+1到k-1所需要添加的最少字符个数+从k+1到j之间所需要添加的最少字符个数(即dp[i+1][k-1] + dp[k+1][j])。因为k可能有多个,所以在其中所有的k的情况中取最小的。
求出两种情况后,这两种之间再求最小值,可以直接把dp[i][j]的初始值赋为dp[i+1][j]+1,然后进行第二种情况的求解
动态转移方程出来之后需要判断写几层循环和每层循环的起点和终点,从方程可以看出,有3层循环(i, j, k),对于dp[i][j]来说,必须先求出i之后的行的前j个,所以有两种循环的方法,我用的是第二种方法,感觉相对好理解。如下图。
方式一:从左往右更新 方式二:从下往上更新
初始化:只有一个字符时至少添加一个字符,所以dp[i][i] = 1;
附上AC代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int dp[110][110]; char s[110]; bool judge(int i, int j)//判断i,j位置是否匹配 { if ((s[i]==‘(‘ && s[j]==‘)‘) || (s[i]==‘[‘ && s[j]==‘]‘)) return true; else return false; } int main() { int n; scanf("%d", &n); while (n--) { scanf("%s", s+1); memset(dp, 0, sizeof(dp)); int len = strlen(s+1); for (int i=1; i<=len; i++) dp[i][i] = 1; for (int i=len-1; i>=1; i--)//从下至上 { for (int j=i; j<=len; j++)//从左至右 { dp[i][j] = dp[i+1][j]+1;//赋初值为第一种情况 for (int k=i+1; k<=j; k++) { if (judge(i, k)) dp[i][j] = min(dp[i][j], dp[i+1][k-1]+dp[k+1][j]); } } } printf("%d\n", dp[1][len]); } return 0; }
NYOJ 15 括号匹配(二) dp
时间: 2024-10-08 19:01:04