动态规划 POJ1141 Brackets Sequence 解题报告

原题链接http://poj.org/problem?id=1141

 

Description

Let us define a regular brackets sequence in the following way:

1. Empty sequence is a regular sequence.
2. If S is a regular sequence, then (S) and [S] are both regular sequences.
3. If A and B are regular sequences, then AB is a regular sequence.

For example, all of the following sequences of characters are regular brackets sequences:

(), [], (()), ([]), ()[], ()[()]

And all of the following character sequences are not:

(, [, ), )(, ([)], ([(]

Some sequence of characters '(', ')', '[', and ']' is given. You are to find the shortest possible regular brackets sequence, that contains the given character sequence as a subsequence. Here, a string a1 a2 ... an is called a subsequence of the string b1 b2 ... bm, if there exist such indices 1 = i1 < i2 < ... < in = m, that aj = bij for all 1 = j = n.

Input

The input file contains at most 100 brackets (characters '(', ')', '[' and ']') that are situated on a single line without any other characters among them.

Output

Write to the output file a single line that contains some regular brackets sequence that has the minimal possible length and contains the given sequence as a subsequence.

Sample Input

([(]

Sample Output

()[()]

Source

Northeastern Europe 2001
  题目意思就是说给一个不规则的字符串,求添加最少的字符使其成为一个规则的字符串。规则的字符串题目中已经给出了定义。本题在LRJ的书上是作为dp的第一个例题讲的,看起来十分简单,其实我感觉对于dp初学者来说,难度还是很大的。LRJ的书上虽然给出了解题的思路,却没有给出完整的答案,只是通过这个题告诉你什么是dp。本题的解法就是通过求序列s[i][i+1]...s[j]最少需要添加的括号d[i][j]来求最终的字符串。但是书上讲得非常模糊,甚至在113页的代码还有一点小错误,所以并不十分易懂。甚至会给初学者造成困扰。
  本人的想法如下:
  对于任何s[i]..s[j]应该分为两种情况考虑,一种是s[i]='('&&s[j]=')' 或者s[i]='['&&s[j]=']',如果是这种情况,则d[i][j]=d[i+1][j-1],则i,j处不需要添加括号。做一标记v[i][j]=-1;即可。还有一种情况就是上述条件不满足,则可以把s[i]..s[j]分成两段考虑,枚举i,j中间的点k,i=<k<j;即可。然后取d[i][j]=min(d[i][j],d[i][k]+d[k+1][j]);同时应该对所取得k进行标记v[i][j]=k。
  然后就是输出问题,对任意的s[i]..s[j],对其输出,如果i>j则输出结束。否则分几种情况考虑:1.i=j;则此时i,j指的就是一个单个字符,如果s[i]='('or')'直接输出一对括号即可。等于'['同上。但是一般的情况都是i,j中间还有字符,要分两种情况,如果v[i][j]=-1;即标记时s[i]、s[j]是一对,此时输出s[i],s[i+1]..s[j-1],s[j]即可。如果更一般的情况即v[i][j]不等于-1的时候就是说s[i]..s[j]已经在i,j中间分开,那么仍然是递归输出s[i]...s[v[i][j]],s[v[i][j]+1]...s[j]即可。至此输出完成。
  以上仅为个人的一点看法。dp是算法里面最难掌握的部分之一,需要大量的练习。
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
string s;
int d[101][101], value[101][101], length;
void fun()
{
	for (int p = 0; p < length; p++)d[p][p] = 1;
	for (int tem = 1; tem < length; tem++)
		for (int i = 0; i < length - tem; i++)
		{
			int j = tem + i;
			d[i][j] = 99999;
			if ((s[i] == '(' && s[j] == ')') || (s[i] == '[' && s[j] == ']'))//1@
			{
				d[i][j] = d[i + 1][j - 1];
				value[i][j] = -1;
			}
			for (int k = i; k < j; k++)//此处需要注意,即便1@处成立,也不能省略
				if (d[i][j] > d[i][k] + d[k + 1][j])
				{
					d[i][j] = d[i][k] + d[k + 1][j];
					value[i][j] = k;
				}
		}
		return;
}
void print(int i, int j)
{
	if (i > j)return;
	else if (i == j)
	{
		if (s[i] == '(' || s[i] == ')')	printf("()");
		if (s[i] == '[' || s[i] == ']')printf("[]");
	}else if (value[i][j] == -1)
	{
		printf("%c", s[i]);
		print(i + 1, j - 1);
		printf("%c", s[j]);
	}else {
		print(i, value[i][j]);
		print(value[i][j] + 1, j);
	}
	return;
}
int main()
{
	while (getline(cin, s))
	{
		memset(d, 0, sizeof(d));
		length = (int)s.length();
		fun();
		print(0, length - 1);
		printf("\n");
	}
	return 0;
}
posted @ 2011-02-12 16:31 like@neu 阅读(...) 评论(...) 编辑 收藏