树、贪心、表达式求值

科技树缺失力(悲)。

考虑对于给定的运算序列建表达式树,举个例子 1|1&0|1&0(值为 $1$)。

建树之后仅需一遍 dfs 即可求出两种运算的短路次数,问题转化为在 $O(n)$ 时间复杂度内根据给定中缀表达式建立表达式树。注意由于 & 运算的优先级更高,对于没有括号的式子应当先将 & 运算符提出来连边,然后再从左到右处理 | 运算符。

中缀表达式不好处理,转成后缀表达式之后再建表达式树。转换过程如下。

从左到右扫描中缀表达式,使用 $c$ 表示当前字符。

  • 若 $c$ 既不是括号又不是运算符,将 $c$ 追加到后缀表达式末尾。
  • 若 $c$ 为 (,将 $c$ 放入存储运算符的栈中。
  • 若 $c$ 为 ),将运算符栈顶不断追加到后缀表达式末尾后弹出,在栈顶为 ( 时停止。将栈顶的 ( 弹出。
  • 若 $c$ 为运算符,将运算符栈顶不断追加到后缀表达式末尾后弹出,在栈为空、栈顶为 ( 或栈顶运算符优先级严格低于 $c$ 时停止。将 $c$ 放入存储运算符的栈中。

扫描结束后将运算符栈中的元素依次追加至后缀表达式末尾

这么一来 1|1&0|1&0 就变成 110&|10&| 了。

这样看不太合理,看看 1+2*3*(5*3-2)-100 的变化,最终这个式子变成 123*53*2-*+100-,手动模拟可得实际计算顺序与中缀表达式相同。

计算后缀表达式需要维护一个栈存储数字,从左到右扫描表达式,如果当前字符 $c$ 是数字就将其入栈,否则 $c$ 是运算符,取出栈顶的两个数字,进行运算后将结果压入栈中。最后栈中仅剩的一个数字就是后缀表达式的值。

由于要计算短路数,所以要建出表达式树,具体过程与计算后缀表达式的值相同。

建出表达式树后一次 dfs 求解,每次递归完左子树后根据左子树节点值和运算符判断是否短路即可。注意被多次使用的栈 sta 不能为 char 类型,否则 $n \geq 128$ 时会爆炸

时间复杂度 $O(n)$。

#include<bits/stdc++.h>
using namespace std;
int n,dan,dor,top,sta[1'000'005],son[1'000'005][2];
char c[1'000'005],s[1'000'005];
int dfs(int x){
    if(son[x][0]==0) return s[x]-'0';
    int ret=dfs(son[x][0]);
    if(s[x]=='&'){
        if(ret==0) return ++dan,0;
        return dfs(son[x][1]);
    }
    else{
        if(ret==1) return ++dor,1;
        return dfs(son[x][1]);
    }
}
int main(){
    freopen("expr.in","r",stdin);
    freopen("expr.out","w",stdout);
    scanf("%s",c+1);
    for(int i=1;c[i]!='\0';i++){
        if(isdigit(c[i])) s[++n]=c[i];
        else if(c[i]=='(') sta[++top]=c[i];
        else if(c[i]==')'){
            while(sta[top]!='('){
                s[++n]=sta[top];
                --top;
            }
            --top;
        }
        else{
            while(top&&sta[top]!='('&&c[i]>=sta[top]){
                //char(&) < char(|)
                s[++n]=sta[top];
                --top;
            }
            sta[++top]=c[i];
        }
    }
    while(top) s[++n]=sta[top],--top;
    s[n+1]='\0';
    for(int i=1;i<=n;i++){
        if(isdigit(s[i])) sta[++top]=i;
        else{
            son[i][1]=sta[top],--top;//注意左右子树顺序
            son[i][0]=sta[top],--top;
            sta[++top]=i;
        }
    }
    int ans=dfs(sta[top]);
    printf("%d\n%d %d\n",ans,dan,dor);
    return 0;
}
最后修改:2024 年 11 月 26 日
如果觉得我的文章对你有用,请随意赞赏