POJ2955 Brackets —— 区间DP
题目链接:https://vjudge.net/problem/POJ-2955
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 9630 | Accepted: 5131 |
Description
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.
Output
For each input case, the program should print the length of the longest possible regular brackets subsequence on a single line.
Sample Input
((())) ()()() ([]]) )[)( ([][][) end
Sample Output
6 6 4 0 6
Source
题解:
求最多有多少对括号匹配。典型的区间dp。
方法一:
1.如果区间[l,r]的两端匹配,则左右各缩进一格,从而转化成处理[l+1, r-1]的区间。
2.不管是否符合条件1,都尝试去枚举分割点,使得整个区间分成两半,这样就可以把大区间的处理转化成两个小区间的处理。
记忆化搜索:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int MOD = 1e9+7; 17 const int MAXN = 100+10; 18 19 20 char s[MAXN]; 21 int dp[MAXN][MAXN]; 22 23 int dfs(int l, int r) 24 { 25 if(r<=l) return 0; 26 if(dp[l][r]!=-1) return dp[l][r]; 27 28 if( (s[l]=='('&&s[r]==')')||(s[l]=='['&&s[r]==']') ) //如果两端匹配,则可以缩减范围 29 dp[l][r] = dfs(l+1, r-1) + 1; 30 for(int k = l; k<r; k++) //枚举分割点,分成两半 31 dp[l][r] = max(dp[l][r], dfs(l, k)+dfs(k+1, r)); 32 33 return dp[l][r]; 34 } 35 36 int main() 37 { 38 while(scanf("%s", s+1) && strcmp(s+1, "end")) 39 { 40 memset(dp, -1, sizeof(dp)); 41 cout<< dfs(1, strlen(s+1))*2 <<endl; 42 } 43 }
递推:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int MOD = 1e9+7; 17 const int MAXN = 100+10; 18 19 char s[MAXN]; 20 int dp[MAXN][MAXN]; 21 22 int main() 23 { 24 while(scanf("%s", s+1) && strcmp(s+1, "end")) 25 { 26 memset(dp, 0, sizeof(dp)); 27 int n = strlen(s+1); 28 for(int len = 2; len<=n; len++) 29 { 30 for(int l = 1; l<=n-len+1; l++) 31 { 32 int r = l+len-1; 33 if( (s[l]=='('&&s[r]==')') || (s[l]=='['&&s[r]==']') ) 34 dp[l][r] = dp[l+1][r-1] + 1; 35 for(int k = l; k<r; k++) 36 dp[l][r] = max(dp[l][r], dp[l][k]+dp[k+1][r]); 37 } 38 } 39 printf("%d\n", dp[1][n]*2); 40 } 41 return 0; 42 }
方法二:
1.可知一个符号最多只能与一个符号匹配,那么对于当前的符号,我们就枚举其他符号与其匹配(不管是能匹配成功)。
2.假设区间为 [l, r],为l枚举匹配符号,当枚举到k位置时,就把区间分割成了两部分:[l+1, k-1] 和 [k+1, r] 。从而就把大区间的求解转化为小区间的求解。
记忆化搜索:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int MOD = 1e9+7; 17 const int MAXN = 100+10; 18 19 char s[MAXN]; 20 int dp[MAXN][MAXN]; 21 22 int dfs(int l, int r) 23 { 24 if(r<=l) return 0; 25 if(dp[l][r]!=-1) return dp[l][r]; 26 27 dp[l][r] = dfs(l+1, r); 28 for(int k = l+1; k<=r; k++) 29 { 30 int ismatch = (s[l]=='('&&s[k]==')')||(s[l]=='['&&s[k]==']'); 31 int tmp = dfs(l+1, k-1)+dfs(k+1, r)+ismatch; 32 dp[l][r] = max(dp[l][r], tmp); 33 } 34 return dp[l][r]; 35 } 36 37 int main() 38 { 39 while(scanf("%s", s+1) && strcmp(s+1, "end")) 40 { 41 memset(dp, -1, sizeof(dp)); 42 cout<< dfs(1, strlen(s+1))*2 <<endl; 43 } 44 }
递推:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int MOD = 1e9+7; 17 const int MAXN = 100+10; 18 19 char s[MAXN]; 20 int dp[MAXN][MAXN]; 21 22 int main() 23 { 24 while(scanf("%s", s+1) && strcmp(s+1, "end")) 25 { 26 memset(dp, 0, sizeof(dp)); 27 int n = strlen(s+1); 28 for(int len = 2; len<=n; len++) 29 { 30 for(int l = 1; l<=n-len+1; l++) 31 { 32 int r = l+len-1; 33 dp[l][r] = dp[l+1][r]; 34 for(int k = l+1; k<=r; k++) 35 { 36 int ismatch = (s[l]=='('&&s[k]==')')||(s[l]=='['&&s[k]==']'); 37 dp[l][r] = max(dp[l][r], dp[l+1][k-1]+dp[k+1][r]+ismatch); 38 } 39 } 40 } 41 printf("%d\n", dp[1][n]*2); 42 } 43 return 0; 44 }