括号类问题总结 持续更新

前言

本来说今天要写写状压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\n");
                return ;
            }
        }else {
            if(!top||ch(str[i])+ch(stk[top])!=9){
                printf("NO\n");
                return;
            }
            top--;
        }
    }
    if(top)printf("NO\n");
    else printf("YES\n");
}
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\n",ans);
}

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

posted @ 2020-04-28 07:40  An_Fly  阅读(262)  评论(0编辑  收藏  举报