POJ1141题解

题目链接1

题目链接2

题目大意

给出一个括号序列,添加最少的括号使序列正确

解题思路

先将问题简单化,从求序列退化为求最小添加括号数的问题

用区间dp n³解决

f[l][r]表示使第l个到r个区间正确的最小添加数

1 :当l = r时, f[l][r] = f[l+1][r-1]

2 :  在l到j中,枚举中间点k,则f[l][r] = min (f[l][r], f[l][k] + f[k+1][r])

求出了最小添加括号数后,再来思考完整的问题

用递归解决输出方案

假设有一个 从 l 到 r 的区间

这个区间的最优解有两种情况:

1:有上述第1种情况求得

2:由上述第2种情况求得

对于 1, 先输出最左边的字符,再递归中间部分,再输出最右边的字符

对于2, 用w数组记录此时最优方案的分割点k,分别递归左半边和右半边

特别情况,当l = r 则需在这个地方添加一个括号与其配对 

注意

要对读入序列长度为0进行特判

不然会很惨

几个小时也调不出来qwq

完整代码加注释

突然发现我的代码好短

#include <bits/stdc++.h>
using namespace std;
char ch[105];
int f[105][105], w[105][105];
void out (int x) {
	if (ch[x] == '(' or ch[x] == ')')  cout << "()";
	else cout << "[]";
}//输出与单个括号配对的函数 
void print (int l, int r) {
	if (l > r) return;
	else if (l == r)  out (l);  //若l = r 则需在这个地方添加一个括号与其配对 
	else if (w[l][r] == 0)  cout << ch[l],  print (l + 1, r - 1),  cout << ch[r];  //上述情况1 
	else  print (l, w[l][r]),  print (w[l][r] + 1, r);   //上述情况2 
} //输出方案递归函数 
int main(){
    scanf ("%s", ch + 1);
    int len = strlen (ch + 1);
    if (!len) puts("\n");      //当长度为0的特判 
    memset (f, 0x3f, sizeof (f));
    for (int i = 1; i <= 101; i++)  f[i][i] = 1;  //长度为1的区间初值赋值为1 
    for (int l = 2; l <= len; l++)   //区间dp 
      for (int i = 1; i <= len - l + 1; i++) {
      	int j = i + l - 1;
      	if ((ch[i] == '[' and ch[j] == ']') or (ch[i] == '(' and ch[j] == ')'))  
		  if (l != 2) f[i][j] = f[i+1][j-1];  else f[i][j] = 0; //当这两个可以匹配的情况,注意:当长度为2时要特判 
      	for (int k = i; k < j; k++)  
		  if (f[i][j] > f[i][k] + f[k+1][j])  f[i][j] = f[i][k] + f[k+1][j], w[i][j] = k;  //枚举并记录下最优方案的分割点 
	  }
    print (1, len);
    return 0;
}

 

猜你喜欢

转载自www.cnblogs.com/whx666/p/10624879.html
今日推荐