模拟 + 栈 + DP:括号

题目链接:登录—专业IT笔试面试备考平台_牛客网

分析:

这道题目写的时候有一个地方没有想到,就是左边的括号如果还有没有配对的情况,那么他也可以与右边的括号进行配对。

如:( ( (  ( )  (( )) ()  ) ) ) 

红色的括号进行配对的情况当时写的时候没有考虑到。

栈:用于存储前面还剩下多少个“ ( ”没有被配对。 因为配对的话," ) "一定是和 最近的" ( "进行配对的。剩下的用栈进行存储,后进先配对。

当在次遇到右括号,那么就将右括号 与 栈顶的‘( ’去进行配对。

当右括号被配对完了,而左括号可以刚好配对玩,或者还没配对完。会有什么样的情况呢?

( ( (  ) ) 

比如这样的情况

那么这个时候,如果( ( (  ) )  () 这样的时候,

那么就会有( (  ) ) , () , ( (  ) )  ()就说明后面新配对的 (),与前面那个右括号配对完的情况,可以恰好组成一组。

在比如: 

() (( )(( () () )) )

而这个红色的就会与((   )) 可以与( )的进行匹配,( ) 可以与()进行连续的匹配。

而这个就可以通过DP进行统计。

代码实现:

# include <iostream>
# include <stack>
using namespace std;

const int N = 1e6 + 10;

/* 
如果多出来了左括号,可能后面会有右括号可以与其进行匹配,
但是如果前面多出来了右括号,
那么后面多出来的左括号和右括号都没有用,不能组成成立的情况
*/

long long f[N]; // 后面的偶数项,可以与前面多少个进行配对

long long a[N];
stack<pair<long long,long long> > q; //q.first为左括号节点的下标 以及个数

long long n;

int main()
{
    scanf("%lld",&n);
    for(int i = 1 ; i <= n ;i++)
    {
        scanf("%lld",&a[i]);
    }
    
    long long ans = 0; //答案
    
    for(int i = 1 ; i <= n ; i++)
    {
        if(i % 2) // 左括号
        {
            q.push({i,a[i]}); // 将左括号的下标和左括号个数存入
        }
        else // 右括号,那么和左边剩下来的左括号进行匹配。两个情况,1.要么左边括号全部匹配掉了,剩下右括号,那么这个右括号没有用。2.剩下左括号,右括号被匹配完了,那么左括号还可以继续用
        {
            if(a[i] == 0) // 此题不需要,因为此题要求a[i]都 > 0 ,但如果a[i]可以等于0的话,那么就需要加这句话,因为后面的f[i] = f[last] + 1 是在a[i] == 0,且原a[i]右括号是可以组合成功的时候才满足f[i] = f[last] + 1的,如果a[i]原来就等于0,那么这是不满足组合情况的,这个地方就得断开,所以f[i]必定为0
            {
                continue;
            }
            
            long long last = 0; //最后一个将右括号完全匹配的左括号的下标 - 1,用于后面f[i] 用 f[last]进行更新
            while(q.size()) // 栈中的左括号,从最后的左括号向最旧的左括号,不断的去与右括号匹配
            {
                auto temp = q.top();
                long long idx = temp.first; // 左括号位置下标
                long long v = temp.second; // 这个位置的左括号个数
                q.pop();
                if(v <= a[i]) //如果当前这个左括号比右括号少,那么还可以继续匹配
                {
                    ans += v; // 左括号与右括号单独匹配的个数
                    
                    a[i] -= v; // 剩下右括号数
                    
                    ans += f[idx - 1]; // 这个f[idx - 1]的含义就是,前面可以与i匹配成功
                    //idx的左括号可以与i的右括号匹配,那么f[idx - 1]统计的则是前面已经匹配成功的个数。可以构成这么多个子串
                    
                    last = idx - 1; // 最后匹配成功的左括号的下标 - 1
                }
                else // 左边括号有剩余,并且当前这个左括号下标对应的左括号也还有剩余,所以不可能构成别的新的连续满足条件的序列,所以last = 0
                {
                    ans += a[i];
                    
                    v -= a[i]; // 剩余左括号的数
                    a[i] = 0;
                    
                    last = 0; // 当前这个位置都还有左括号,无法向左边构成新的子序列。
                    
                    q.push({idx,v}); //重新压栈
                    
                }
                if(a[i] == 0)
                {
                    break;
                }
            }
            if(a[i] == 0) // 右括号被匹配完了,说明左括号成立
            {
                f[i] = f[last] + 1; // 左边最last的情况 + 1.
            }
            // 而a[i] > 0 则说明还有右括号,没用
        }
    }
    
    printf("%lld\n",ans);
    return 0;
}

/*
如果a[]值可以为0
数据:
4
1 0 1 2
结果为:2
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值