Brackets Sequence POJ - 1141

发布时间 2023-05-01 08:56:44作者: 腾云今天首飞了吗

题意

咕咕是一只神奇的咕咕,虽然他很讨厌字符串但是他喜欢看别人做字符串的题目,现在咕咕给了你一个只含有'(’ 和‘)’和'['和']'的字符串,现在让你把他变成正则序列。
正则序列:

  1. 空序列是正则序列。
  2. 如果S是正则序列,那么(S)和[S]也是。
  3. 如果A是B也是,那么AB也是正则序列。
    输入一个只含有'(', ')', '[', ']'字符的字符串,字符串的最大长度是100。
    输出最少需要多少次变换后的序列(需要换行)。

分析

\(dp[l][r]\) 为将区间 \([l,r]\) 变成括号匹配的序列最少需要的代价。
对于 (())(左右的 _ 表示当前需要填写的括号)。此时 \(dp[l][r]\) 就能从 \(dp[l + 1][r - 1]\) 转移过来。这对应的是第二条规则。
对于第三条规则,只需要在 \([l,r]\) 中枚举一个 \(k\),即枚举一下从哪里断开,分成两个正则序列。
转移的时候需要记录一下每个状态的最优决策点在哪里,便于接下来的递归输出。
总的dp方程如下:

\[dp[l][r] = min(dp[l][r],min(dp[l + 1][r - 1],dp[l][k] + dp[k + 1][r])) \]

然后递归输出就行了。

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
const int MAXN = 2e3;
const int INF = 1e9;
string s;
int dp[MAXN][MAXN],pos[MAXN][MAXN];
void print(int i,int j){
	if(i > j)return;
	if(i == j){
		if(s[i] == '(' || s[i] == ')')cout << "()";
		else if(s[i] == '[' || s[i] == ']')cout << "[]";
		return;
	}
	if(pos[i][j] == -1){
		cout << s[i];
		print(i + 1,j - 1);
		cout << s[j];
	}
	else if(pos[i][j] != -1){
		print(i,pos[i][j]);
		print(pos[i][j] + 1,j);
	}
}
int main(){
	while(getline(cin,s)){
		if(s.size() == 0){
			puts("");
			continue;
		}
		memset(dp,0,sizeof dp);
		memset(pos,0,sizeof pos);
		for(int i = 0; i < s.size(); i++){
			dp[i][i] = 1;
		}
		for(int l = 1; l < s.size(); l++){
			for(int i = 0; i + l < s.size(); i++){
				int j = i + l;
				dp[i][j] = INF;
				if(s[i] == '(' && s[j] == ')'){
					dp[i][j] = min(dp[i][j],dp[i + 1][j - 1]);
					pos[i][j] = -1;
				} 
				else if(s[i] == '[' && s[j] == ']'){
					dp[i][j] = min(dp[i][j],dp[i + 1][j - 1]);
					pos[i][j] = -1;
				}
				for(int k = i; k < j; k++){
					if(dp[i][j] > dp[i][k] + dp[k + 1][j]){
						dp[i][j] = dp[i][k] + dp[k + 1][j];
						pos[i][j] = k;
					}
				}
			}
		}
		print(0,s.size() - 1);
		puts("");
	}
}