POJ-1141-Brackets Sequence

img

题目链接:

Go!

题目大意:

​ 给你一个长度不超过 100 100 且只包含 (,),[,] ( , ) , [ , ] 四种字符的字符串 s s ,添加 最少 的字符使其变成一个合法的字符串,输出这个合法的字符串

合法字符串的定义:
  1. Empty sequence s e q u e n c e is i s a a regular sequence. s e q u e n c e .

    • If I f S S is a a regular sequence s e q u e n c e , then t h e n (S) ( S ) and a n d [S] [ S ] are a r e both b o t h regular r e g u l a r sequences. s e q u e n c e s .
    • If I f A A and B B are regular r e g u l a r sequences s e q u e n c e s , then t h e n AB A B is i s a a regular sequence s e q u e n c e .
    • 样例:

      Sample S a m p l e Input I n p u t

      ([(]

      Sample S a m p l e Output O u t p u t

      ()[()]

      解题思路:

      这是一道 区间 dp d p (路径还原)

      • 状态方程 : dp[i][j] d p [ i ] [ j ] 代表使 区间 (i,j) ( i , j ) 形成一个合法的字符串最少需要添加多的字符串?

        path[i][j] p a t h [ i ] [ j ] 代表添加最少的字符使 区间 (i,j) ( i , j ) 形成一个合法的字符串的路径选择。

      • path[i][j] p a t h [ i ] [ j ] = = 1 ,表示头尾。

        path[i][j] p a t h [ i ] [ j ] = = k ( k k != 1 ) ,表示由 区间 (i,k) ( i , k ) 区间 ( k+1 k + 1 , j j ) 而来。

      • 转移方程

        • 区间 (i,j) 可以由 区间 (i,k) ( i , k ) + 区间 (k+1,j) ( k + 1 , j ) 转移而来。

        • s[i] s [ i ] = s[j] s [ j ] 时,区间 (i,j) ( i , j ) 也可以由 区间 (i+1,j1) ( i + 1 , j − 1 ) 转移而来。(需要注意的是:当 i+1=j i + 1 = j 时,直接 dp[i][j] d p [ i ] [ j ] = 0 0 ,否则会出现 i+1>j1 的情况)
      • 小思考:为什么最外层是枚举 区间长度,而不是直接枚举 区间端点 l,r l , r 呢?

        • 答:因为我们转移的时候是从 小区间 转移到 大区间 的。
      • 最后答案:根据 path[i][j] p a t h [ i ] [ j ] 记录的值,用递归还原路径。(详情请看代码)

      • AC代码:

        #include<iostream>
        #include<cstring>
        #include<algorithm>
        #include<cstdio>
        using namespace std;
        
        const int maxn = 105;
        int dp[maxn][maxn],path[maxn][maxn];
        char s[maxn];
        
        void dfs(int l,int r){
            if(l > r) return;
            if(l == r){
                if(s[l] == '(' || s[l] == ')') cout << "()";
                else cout << "[]";
                return;
            }
            if(path[l][r] == -1){
                cout << s[l];//先输出左端点,
                dfs(l + 1,r - 1);//再输出中间,
                cout << s[r];//最后输出右端点。
            }
            else{
                int k = path[l][r];//分两部分输出。
                dfs(l,k);
                dfs(k + 1,r);
            }
        }
        
        int main(){
            int n;
            while(gets(s + 1)){
                n = strlen(s + 1);
                memset(dp,0x3f,sizeof(dp));
                memset(path,0,sizeof(path));
                for(int i = 1;i <= n;i++) dp[i][i] = 1;//初始化:自己当然初始化为 1,其他初始化为无穷大。
                for(int len = 2;len <= n;len++){
                    for(int l = 1;l <= n - len + 1;l++){
                        int r = len + l - 1;
                        if((s[l] == '(' && s[r] == ')') || (s[l] == '[' && s[r] == ']')){
                            if(dp[l + 1][r - 1] < dp[l][r]){
                                dp[l][r] = dp[l + 1][r - 1];
                                path[l][r] = -1;
                            }
                            else if(l + 1 == r){
                                dp[l][r] = 0;
                                path[l][r] = -1;
                            }
                        }
                        for(int k = l;k < r;k++){
                            if(dp[l][k] + dp[k + 1][r] < dp[l][r]){
                                dp[l][r] = dp[l][k] + dp[k + 1][r];
                                path[l][r] = k;
                            }
                        }
                    }
                }
                dfs(1,n);
                cout << endl;
            }
        }
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值