CSP-J 2022」逻辑表达式(expr)题解
步骤一:中缀表达式转后缀表达式
题目中给出的输入格式是中缀表达式,然而要让计算机解决问题,则需将中缀表达式转为后缀表达式。
这里需要用到动态数组和栈来进行转换
stack<char>ops;// 存储运算符
vector<char>sf;// 存储后缀表达式
而如何进行转换呢?有如下几点步骤
- 如果扫到数字,则直接存入后缀表达式,即
sf
- 如果扫到左边括号,即
(
,则将左边括号存入运算符栈,即ops
- 如果扫到右边括号,即
)
,则需先将运算符栈内的运算符不断存入到后缀表达式里并出栈,直到碰到左边括号为止,记得将左边括号出栈 - 如果扫到运算符,则需将运算符栈内的运算符不断存入后缀表达式里,直到碰到比当前运算符优先级低的运算符,或者栈为空,然后再将该运算符入栈
- 结束转换时,记得将运算符栈内剩余的运算符存入后缀表达式并出栈
void change()// 中缀转后缀
{
for(int i=0;i<s.size();i++){
if(s[i]=='0'||s[i]=='1') sf.push_back(s[i]); //扫到数字
else if(s[i]=='(') ops.push(s[i]); // 扫到左边括号
else if(s[i]==')') {
while(!ops.empty()&&ops.top()!='('){
sf.push_back(ops.top());// 一直输出到sf。直到遇上(
ops.pop();
}
ops.pop(); // 输出多余的(
}
else if(s[i]=='&'){ //与
while(!ops.empty()&&ops.top()=='&'){
sf.push_back(ops.top());
ops.pop();
}
ops.push('&');
}
else{ //或
while(!ops.empty()&&ops.top()!='('){
sf.push_back(ops.top());
ops.pop();
}
ops.push('|');
}
}
while (!ops.empty()){// 剩余运算符出栈
sf.push_back(ops.top());
ops.pop();
}
}
步骤二:建表达式树
读取后缀表达式。 如果操作数是符号,那么我们就建立一个单节点树并将一个指向它的指针推入栈中;
如果操作数为数字,则从栈中弹出两棵树 ,并形成一颗以操作符为根节点的树
然后将新的树压入栈中,继续上述过程。
void build()
{
for(int i=0;i<sf.size();i++){
if(sf[i]=='1'||sf[i]=='0'){
tr[++num]={sf[i]-'0',-1,-1};
sta.push(num);
}
else{
int r=sta.top(); sta.pop();
int l=sta.top(); sta.pop();
int v=(sf[i]=='&'?2:3);
tr[++num]={v,l,r};
sta.push(num);
}
}
}
步骤三:深搜表达式树
简单dfs即可,dfs步骤:
- 碰到叶子节点直接返回值
- 碰到非叶子节点时,先遍历左子树的值
- 基于左子树的值和当前节点判断是否发生短路:
1|
,发生或短路0&
,发生与短路- 不发生短路,计算右子树节点并返回值即可
int dfs(int u)
{
if(tr[u].v==0||tr[u].v==1) return tr[u].v; //叶子节点,返回数值
int l=dfs(tr[u].l);
if(l==0&&tr[u].v==2) {//0&
ans1++;
return 0;
}
if(l==1&&tr[u].v==3) {//1|
ans2++;
return 1;
}
int r=dfs(tr[u].r); //如果没走结果取决于右子树
return r;
}
完整代码
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
string s;
const int N=1e6+5;
struct Node{
int v,l,r;
}tr[N];
int num,ans1,ans2;
stack<char>ops;
stack<int>sta;
vector<char>sf;
void change()// 中缀转后缀
{
for(int i=0;i<s.size();i++){
if(s[i]=='0'||s[i]=='1') sf.push_back(s[i]); //扫到数字
else if(s[i]=='(') ops.push(s[i]); // 扫到左边括号
else if(s[i]==')') {
while(!ops.empty()&&ops.top()!='('){
sf.push_back(ops.top());// 一直输出到sf。直到遇上(
ops.pop();
}
ops.pop(); // 输出多余的(
}
else if(s[i]=='&'){ //与
while(!ops.empty()&&ops.top()=='&'){
sf.push_back(ops.top());
ops.pop();
}
ops.push('&');
}
else{ //或
while(!ops.empty()&&ops.top()!='('){
sf.push_back(ops.top());
ops.pop();
}
ops.push('|');
}
}
while (!ops.empty()){
sf.push_back(ops.top());
ops.pop();
}
}
void build()
{
for(int i=0;i<sf.size();i++){
if(sf[i]=='1'||sf[i]=='0'){
tr[++num]={sf[i]-'0',-1,-1};
sta.push(num);
}
else{
int r=sta.top(); sta.pop();
int l=sta.top(); sta.pop();
int v=(sf[i]=='&'?2:3);
tr[++num]={v,l,r};
sta.push(num);
}
}
}
int dfs(int u)
{
if(tr[u].v==0||tr[u].v==1) return tr[u].v; //叶子节点,返回数值
int l=dfs(tr[u].l);
if(l==0&&tr[u].v==2) {//0&
ans1++;
return 0;
}
if(l==1&&tr[u].v==3) {//1|
ans2++;
return 1;
}
int r=dfs(tr[u].r); //如果没走结果取决于r
return r;
}
int main()
{
cin>>s;
change();
build();
cout<<dfs(num)<<endl;
cout<<ans1<<" "<<ans2;
return 0;
}