Luogu P8815 [CSP-J 2022] 逻辑表达式 题解 [ 绿 ] [ 栈 ] [ 表达式树 ] [ DP ]
逻辑表达式:表达式树裸题。
一个显然的思路是对式子建表达式树,然后树形 DP 求解。
但是这个做法太麻烦了,于是考虑不建树,直接在栈上做。因为每次操作都相当于树形 DP 上合并两个节点的 DP 值,所以数字栈里要记录三个值:结果、和短路、或短路。其余正常做表达式处理即可。
答案即为数字栈中最后留下来的数。
时间复杂度 \(O(n)\)。
注意表达式求值的细节:
- 遇到运算符时,比自己优先级更大的要先处理。
- 左括号优先级最低,但是入栈的时候不需要进行任何处理。
- 右括号的时候,要一直弹栈直到左括号。然后把左括号出栈。
- 开头最好加一个左括号,结尾再加一个右括号将所有数字处理完。
- 加法优先级为 \(1\),乘法优先级为 \(2\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
using pii=pair<int,pi>;
char c;
stack<int>op;
stack<pii>num;
void work()
{
int opt=op.top();
op.pop();
pii y=num.top();
num.pop();
pii x=num.top();
num.pop();
pii res;
if(opt==1)
if(x.fi==1)
res={1,{x.se.fi,x.se.se+1}};
else
res={(x.fi|y.fi),{x.se.fi+y.se.fi,x.se.se+y.se.se}};
else
if(x.fi==0)
res={0,{x.se.fi+1,x.se.se}};
else
res={(x.fi&y.fi),{x.se.fi+y.se.fi,x.se.se+y.se.se}};
num.push(res);
}
int main()
{
//freopen("sample.in","r",stdin);
//freopen("sample.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
op.push(0);
while(cin>>c)
{
if(c=='0'||c=='1')
num.push({c-'0',{0,0}});
else if(c=='|')
{
while(op.top()>=1)work();
op.push(1);
}
else if(c=='&')
{
while(op.top()>=2)work();
op.push(2);
}
else if(c=='(')
op.push(0);
else
{
while(op.top()!=0)work();
op.pop();
}
}
while(op.top()!=0)work();
op.pop();
pii ans=num.top();
cout<<ans.fi<<'\n'<<ans.se.fi<<" "<<ans.se.se;
return 0;
}

浙公网安备 33010602011771号