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

题目链接:

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;
}

你可能感兴趣的:(算法,dp,Brackets,ural1183)