CSP-DP专题(区间DP)
题目叙述
We give the following inductive definition of a “regular brackets” sequence:
the empty sequence is a regular brackets sequence,
if s is a regular brackets sequence, then (s) and [s] are regular brackets sequences, and
if a and b are regular brackets sequences, then ab is a regular brackets sequence.
no other sequence is a regular brackets sequence
For instance, all of the following character sequences are regular brackets sequences:
(), [], (()), ()[], ()[()]
while the following character sequences are not:
(, ], )(, ([)], ([(]
Given a brackets sequence of characters a1a2 … an, your goal is to find the length of the longest regular brackets sequence that is a subsequence of s. That is, you wish to find the largest m such that for indices i1, i2, …, im where 1 ≤ i1 < i2 < … < im ≤ n, ai1ai2 … aim is a regular brackets sequence.
Given the initial sequence ([([]])], the longest regular brackets subsequence is [([])].
Input和输入样例
The input test file will contain multiple test cases. Each input test case consists of a single line containing only the characters (, ), [, and ]; each input test will have length between 1 and 100, inclusive. The end-of-file is marked by a line containing the word “end” and should not be processed.
输入样例:
((()))
()()()
([]])
)[)(
([][][)
end
Output和输出样例
For each input case, the program should print the length of the longest possible regular brackets subsequence on a single line.
输出样例:
6
6
4
0
6
思路叙述
题目是一道经典的区间DP问题,可以简称为括号问题。括号问题一般有两个相似的版本,这里会将两外一种也进行叙述:
题解算法
题目给出的求解要求是求出最长的合法括号子串,合法的要求是有能够匹配的"(" 和 “)”或匹配的 “[”
和“]”,或者整个子串为空。
具体的合法子串有下列四种:
如果S为合法的子串,则下列四种也为合法子串:
1、(S):在合法串两侧1有匹配的"(" 和 “)”
2、[S];在合法串两侧1有匹配的 “[” 和 “]”
3、S1S2;两个合法串的拼接也为合法串
4、空串为合法串
方便使用动态规划来解决问题,可以设计dp数组为dp[i][j],表示的意义是从 i 位置到 j 位置的最长子串个数。转移方程可以设计为:
1、由子问题的解合并而来:dp[i][j]=max(dp[i][k]+dp[k+1][j]) (i<k<j)
2、由子串的合法匹配而来:如果在合法串两侧1有匹配的"(" 和 “)”,或是在合法串两侧1有匹配的 “[” 和 “]” ,dp[i][j]=dp[i+1][j-1]+2
取两种情况的最大值即可。
拓展算法
另外一种求解要求是求出最小匹配花费,匹配花费的意思是需要添加多少个括号,来保证添加后的括号串是合法的,合法要求与上述相同。
同样适用动态规划来解决问题,设计dp数组dp[i][j],表示的意义是从 i 位置到 j 位置的保证括号串合法的最小添加括号数量。转移方程可以设计为:
1、由子问题的解合并而来:dp[i][j]=min(dp[i][k]+dp[k+1][j]) (i<k<j)
2、由子串的合法匹配而来:如果在合法串两侧有匹配的"(" 和 “)”,或是在合法串两侧1有匹配的 “[” 和 “]” ,dp[i][j]=dp[i+1][j-1]
3、由子串的添加括号而来:如果在合法串单侧有"(" 或 “)”,或是在合法串单侧有 “[” 和 “]” ,添加一个括号进行匹配即可保证合法,dp[i][j]=dp[i+1][j-1]+1
取以上三种情况的最小值即可。
实现源代码
#include<iostream>
#include<stdio.h>
#include<string>
using namespace std;
const int M=1e7;
string s;
int dp[105][105];
int dp_process()
{
for(int i=0;i<=s.size();i++)
for(int j=0;j<=s.size();j++)
dp[i][j]=0;
for(int length=2;length<=s.size();length++)
for(int x=0;x+length-1<s.size();x++)
{
int max_lit_num=0;
int clarify_num=0;
for(int i=x;i<x+length-1;i++)
{
max_lit_num=max(max_lit_num,dp[x][i]+dp[i+1][x+length-1]);
}
if(s[x]=='(' && s[x+length-1]==')')
clarify_num=dp[x+1][x+length-2]+2;
else if(s[x]=='[' && s[x+length-1]==']')
clarify_num=dp[x+1][x+length-2]+2;
dp[x][x+length-1]=max(clarify_num,max_lit_num);
}
return dp[0][s.size()-1];
}
int main()
{
cin>>s;
while(s!="end"){
int res=dp_process();
printf("%d\n",res);
cin>>s;
}
return 0;
}