【算法设计与数据结构】URAL 1183.Brackets Sequence(区间dp求解)

来源:互联网 发布:中国学术论文数据库 编辑:程序博客网 时间:2024/06/06 19:12

题目链接:

http://acm.timus.ru/problem.aspx?space=1&num=1183

题目大意:

定义正规的括号序列如下:
1. 空序列是一个正规的括号序列
2. 如果S是一个正规的括号序列, 那么(S) 和[S] 也都是正规的括号序列。
3. 如果A和B是正规的括号序列, 那么AB也是一个正规的括号序列。
现给定一个括号序列A(只包含小括号和中括号,可能为空序列),求一个正规括号序列B,使得A包含于B,而且B的长度是满足条件的序列中最小的。

思路:

我们用dp[i][j]来表示包含子序列s[i…j]的最短正规括号序列需要添加的括号数,则由定义的2和3可知:

如果 s[i]与s[j]匹配,

dp[i][j] = min{dp[i][k] + dp[k+1][j], dp[i+1][j-1]},i <= k < j;

否则,

dp[i][j] = min{dp[i][k] + dp[k+1][j]},i <= k < j;

以上便是状态转移方程,不过题目要求输出结果序列,所以我们需要标记分解的位置,然后递归打印答案。这可以通过一个mark数组来实现:如果序列不可分解为两个子序列,则mark[i][j] = -1,如果在k处分解,则mark[i][j] = k。
注意,输入可能为空,所以gets来读取一行。

算法步骤:

1) 初始化边界条件:

//单个括号,必然需要添加1个括号与其匹配dp[i][i] = 1;

2) 根据状态转移方程动态规划求解:

状态转移方程中一共有3个变量:i,j,k,由分解的性质可知,长的序列依赖于短的序列,所以我们要保证短的序列先算。我们在最外层循环设置一个变量l,表示序列的长度,2 <= l <= n,则1 <= i <= n-l+1, j = i+l-1,之后对每个i,用k来遍历划分点,i <= k < j。

for (int l = 2; l <= n; l++){    for (int i = 1; i <= n-l+1 ; i++)    {        int j = i+l-1;        dp[i][j] = INF;        if (s[i-1]=='('&&s[j-1]==')' || s[i-1]=='['&&s[j-1]==']')        {            if (dp[i+1][j-1] < dp[i][j])                    dp[i][j] = dp[i+1][j-1];        }        mark[i][j] = -1;        for (int k = i; k < j; k++)        {                       if (dp[i][k]+dp[k+1][j] < dp[i][j])            {                dp[i][j] = dp[i][k] + dp[k+1][j];                mark[i][j] = k;            }        }    }}

3) 打印结果:

根据递推关系,我们可以用printAns(1, n)递归地打印出我们的结果。对于printAns(i, j)关系如下:
如果 i > j,返回;
如果i = j,打印一对括号,返回;
如果i < j,
如果序列不用划分,则打印s[i],打印printAns(i+1,j-1),再打印s[i]对应的匹配括号;
否则,打印printAns(i,k)和printAns(k+1,j),其中k为划分点。

算法复杂度:

O(n^3)

源程序:

#include <iostream>#include <cstring>#include <algorithm>#include <cstdio>using namespace std;const int INF = 99999999;char s[120];int dp[120][120];int mark[120][120];void printAns(int i, int j){    if (i > j) return;    if (i == j)    {        if (s[i-1] == '(' || s[i-1] == ')')            cout<<"()";        else            cout<<"[]";        return;    }    if (mark[i][j] == -1)    {        if (s[i-1] == '(')        {            cout<<'(';            printAns(i+1, j-1);            cout<<')';        }        else        {            cout<<'[';            printAns(i+1, j-1);            cout<<']';        }    }    else    {        printAns(i,mark[i][j]);        printAns(mark[i][j]+1,j);    }}int main(){    gets(s);    int n = strlen(s);    if (!n)    {        cout<<endl;    }    else    {        memset(dp, 0, sizeof(dp));        for (int i = 1; i <= n; i++)            dp[i][i] = 1;        for (int l = 2; l <= n; l++)        {            for (int i = 1; i <= n-l+1 ; i++)            {                int j = i+l-1;                dp[i][j] = INF;                if (s[i-1]=='('&&s[j-1]==')' || s[i-1]=='['&&s[j-1]==']')                {                    if (dp[i+1][j-1] < dp[i][j])                            dp[i][j] = dp[i+1][j-1];                }                mark[i][j] = -1;                for (int k = i; k < j; k++)                {                    if (dp[i][k]+dp[k+1][j] < dp[i][j])                    {                        dp[i][j] = dp[i][k] + dp[k+1][j];                        mark[i][j] = k;                    }                }            }        }        printAns(1, n);        cout<<endl;     }    return 0;}
0 0
原创粉丝点击