VJ传送门:https://cn.vjudge.net/problem/UVA-1626
题目描述:
定义合法的括号序列如下:
1 空序列是一个合法的序列
2 如果S是合法的序列,则(S)和[S]也是合法的序列
3 如果A和B是合法的序列,则AB也是合法的序列
例如:下面的都是合法的括号序列
(), [], (()), ([]), ()[], ()[()]
下面的都是非法的括号序列
(, [, ), )(, ([)], ([(]
给定一个由'(', ')', '[', 和 ']' 组成的序列,找出以该序列为子序列的最短合法序列。
思路:
i-j表示的是一条序列的开始和结束,dp[ i ][ j ]表示子串s[ i~j ] 需要添加的数量。
思想是不断分割小区间,当出现(X)时,应该转移到x,即从dp(i,j)转移到dp(i+1,j-1)
如果为单个字符,则dp[ i ][ j ] = 1;(i == j)
打印:算出最优的地方 1、如果可以直接往里转移,则 dp[ i ][ j ] == dp[ i+1 ][ j-1 ];把s[i]和s[j]打印了,再向里递归print(i+1, j-1);
2、s[i]!=s[j],则直接去找i-j之间的最优点k进行切分,然后递归
收获:递归打印出dp结果的最优解!新技能get!
另:输入输出略坑。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
int dp[105][105], n;
char s[105];
bool match(char p, char q)
{
if(p == '(' && q == ')')
return true;
if(p == '[' && q == ']')
return true;
return false;
}
void getdp()
{
for(int i = 0; i < n; i++)
{
dp[i+1][i] = 0;
dp[i][i] = 1;
}
for(int i = n - 2; i >= 0; i--)
for(int j = i+1; j < n; j++)
{
dp[i][j] = n;
if(match(s[i], s[j]))
dp[i][j] = min(dp[i][j], dp[i+1][j-1]);
for(int k = i; k < j; k++)
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j]);
}
}
void print(int i, int j)
{
if(i > j)
return ;
if(i == j)
{
if(s[i] == '(' || s[i] == ')')
printf("()");
else
printf("[]");
return ;
}
int ans = dp[i][j];
if(match(s[i], s[j]) && ans == dp[i+1][j-1])
{
printf("%c", s[i]);
print(i+1, j-1);
printf("%c", s[j]);
return ;
}
for(int k = i; k < j; k++)
{
if(ans == dp[i][k] + dp[k+1][j])
{
print(i, k);
print(k+1, j);
return ;
}
}
}
int main()
{
int T;
scanf("%d", &T);
getchar();
while(T--)
{
gets(s);
gets(s);
n = strlen(s);
getdp();
print(0, n-1);
puts("");
if(T)
puts("");
}
return 0;
}