POJ 1141 Brackets Sequence(区间DP, DP打印路径)
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
思路:
1. dp[i][j] 表示 子序列 [i, j] 之间的最小添加括号数
2. dp[i][j] = min(dp[i][k], dp[k+1][j]) k = [i, j]
3. choose[i][j] 表示在 dp[i][j] 中的那个位置切割比较合适, 合适的定义是 dp[i][j] > dp[i][k]+dp[k+1][j]
4. 对 dp[i][j] 的计算, 第一种思路是记忆化搜索, 当时还没考虑到 choose 数组. 对于 choose 数组的求解, 记忆化搜索不能实现. 代码里提供的是基于递推的求解过程, 这种遍历方法我也曾做过, 叫做斜对角线更新, 具体是哪道题目也记不清了, blog 我是有写过的
总结:
1.区间 DP
2. 这个地方 WA 了下, 起初写成 k<=j
for(int k = i; k < j; k++) {
if(dp[i][j] > dp[i][k]+dp[k+1][j]) { // that's why/where need special judge
choose[i][j] = k;
dp[i][j] = dp[i][k]+dp[k+1][j];
}
}
update 2014年3月15日14:43:10
3. 类似的题目有 Leetcode palindrome cut, 并且 palindrome cut 是在原始区间 DP 的基础上加上了一些优化. 矩阵乘法也算是区间 DP
代码:
#include <iostream> using namespace std; const int INF = 0X3F3F3F3F; const int MAXN = 110; int choose[MAXN][MAXN]; int dp[MAXN][MAXN]; char s[MAXN]; void printPath(const int &i, const int &j) { if(j < i) return ; if(i == j) { if(s[i] == '(' || s[i] == ')') { cout << "()"; return; }else if(s[i] == '[' || s[i] == ']') { cout << "[]"; return; } } if(choose[i][j] == -1) { // 不需要切割 cout << s[i]; printPath(i+1, j-1); cout << s[j]; }else{ int k = choose[i][j]; printPath(i, k); printPath(k+1, j); } } int main() { //freopen("E:\\Copy\\ACM\\测试用例\\in.txt", "r", stdin); gets(s); int st = 0, ed = strlen(s); memset(dp, 0x3F, sizeof(dp)); for(int i = st; i < ed; i ++) dp[i][i] = 1, dp[i+1][i] = 0; for(int p = 1; p < ed-st; p ++) { for(int i = 0, j = i+p; j < ed; i++, j++) { choose[i][j] = -1; if((s[i] == '(' && s[j] == ')') || (s[i] == '[' && s[j] == ']')) { dp[i][j] = min(dp[i][j], dp[i+1][j-1]); // 需要考虑 dp[j][i] = 0 } for(int k = i; k < j; k++) { if(dp[i][j] > dp[i][k]+dp[k+1][j]) { // that's why/where need special judge choose[i][j] = k; dp[i][j] = dp[i][k]+dp[k+1][j]; } } } } printPath(0, ed-1); cout << endl; return 0; }