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 i1i2, …, im where 1 ≤ i1 < i2 < … < im ≤ nai1ai2… 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;
}

  

原文地址:https://www.cnblogs.com/xinsheng/p/3452828.html