• 括号类问题总结 持续更新


    前言

    本来说今天要写写状压dp的,结果大致看了几道题,发现剩下的都不会,然后突发奇想去看了看去年的CSP-S的题,由于那会儿我只学了不到三个月吧,所以啥都不会借口,就打了40多分,惨。今天重新做了一下,D1T1,这么简单的题为什么我当时不会!其实就是一个分治,写了不到十分钟就A了。主要是想说一下T2,T3仍然不会,T2应该算是一个很经典的问题了,括号配对,只不过这个题是挪到了树上,用一个有辅助的树形dp就能解决,这个括号类的问题值得总结一下。

    裸的配对

    括号配对
    感觉最基础的配对只要好好学过奥赛应该都会,这道题算是难一点的了。
    括号配对时用要用到一个栈,为什么是栈呢?因为栈的特点是后进先出,如果遇到一个括号,那么它肯定和之前遇到的最近的同类型括号匹配,所以用个栈,这道题的难点是它要考虑括号的大小问题,为了解决这个问题,可以根据题意确定一下每个括号的优先级,然后先比较优先级,再进行配对。
    左括号与右括号的优先级顺序应该是正好相反的,注意一下就行。
    还有建议写一个函数来判断能否配对,这样比较方便return。

    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=300;
    char stk[N],str[N];
    int top;
    int ch(char x){
        if(x=='<')return 1;
        if(x=='(')return 2;
        if(x=='[')return 3;
        if(x=='{')return 4;
        if(x=='}')return 5;
        if(x==']')return 6;
        if(x==')')return 7;
        if(x=='>')return 8;
    }
    void judge(){
        top=0;
        int l=strlen(str+1);
        for(int i=1;i<=l;i++){
            if(ch(str[i])<=4){
                if(!top||ch(str[i])<=ch(stk[top]))
                    stk[++top]=str[i];
                else {
                    printf("NO
    ");
                    return ;
                }
            }else {
                if(!top||ch(str[i])+ch(stk[top])!=9){
                    printf("NO
    ");
                    return;
                }
                top--;
            }
        }
        if(top)printf("NO
    ");
        else printf("YES
    ");
    }
    int main(){
        int T;
        scanf("%d",&T);
        while(T--){
            scanf("%s",str+1);
            judge();
        }
    }
    

    稍作变形

    然后其实D1T2的括号树就是由最基本的原型拓展来的。
    先考虑链的情况,因为链最基本并且也有一部分部分分。如果设(f_u)表示从根节点到(u)合法括号序列总数,(p_u)表示(u)的父亲,那么显然有转移方程(f_u=f_{p_u}+以u为右端点的合法括号序列数)(f_{p_u})在dfs的时候就可以求出来,关键是后边那个怎么求,我们可以利用拆分的思想,把一条链拆成两部分,开一个栈存可以被用来配对的左括号的节点编号,假设在(v)遇到一个右括号,如果栈为空,不需要任何操作,因为什么配对都做不了,如果栈不空呢?
    如果它不空的话,取出栈顶元素(u),以u为右端点的合法括号序列数就是以(p_u)为右端点的合法括号序列数+1,加的一就是括号序列(uv),那么之前的还用考虑吗?不用,因为它已经被包含在这种情况里边了。
    链的问题解决了,树上边就只需要一个回溯,因为每层dfs最多只会改变一个元素,所以最后再还原一下现场就行。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=5e5+10;
    struct Edge{
        int to,nxt;
    }e[N];
    int h[N],idx,p[N],top,stk[N];
    char str[N];
    ll f[N],g[N];
    void Ins(int a,int b){
        e[idx].to=b;e[idx].nxt=h[a];h[a]=idx++;
    }
    void dfs(int u){
        if(str[u]=='('){
            stk[++top]=u;
            f[u]=f[p[u]];
            for(int i=h[u];~i;i=e[i].nxt)dfs(e[i].to);
            top--;
        }else {
            if(!top){
                f[u]=f[p[u]];
                for(int i=h[u];~i;i=e[i].nxt)
                    dfs(e[i].to);
            }else {
                int now=stk[top--];
                g[u]=g[p[now]]+1;
                f[u]=f[p[u]]+g[u];
                for(int i=h[u];~i;i=e[i].nxt)
                    dfs(e[i].to);
                stk[++top]=now;
            }
        }
    }
    int main(){
        int n;
        memset(h,-1,sizeof(h));
        scanf("%d%s",&n,str+1);
        for(int i=2;i<=n;i++){
            scanf("%d",&p[i]);
            Ins(p[i],i);
        }
        dfs(1);
        ll ans=0;
        for(int i=1;i<=n;i++)
            ans^=i*f[i];
        printf("%lld
    ",ans);
    }
    

    从这个代码开始图论我要换码风了,Head用h代替,len用idx,别问为什么,问就是跟别的dalao学的。

  • 相关阅读:
    数列分段
    2020-01-21 数组 最大子序和
    2020-01-21 数组
    补 2020-01-20 数组 删除排序数组中的重复项
    补2020-01-19 数组 两数之和
    2020-01-18 刷题 螺旋矩阵 II
    2020-01-16 刷题 长度最小的子数组
    2020-01-15 刷题 移除元素
    2020-01-14 QT学习记录
    2020-01-14 数组刷题-1
  • 原文地址:https://www.cnblogs.com/anyixing-fly/p/12791650.html
Copyright © 2020-2023  润新知