CSP-J 2022」逻辑表达式(expr)题解

题目传送门:洛谷P8815temege

步骤一:中缀表达式转后缀表达式

题目中给出的输入格式是中缀表达式,然而要让计算机解决问题,则需将中缀表达式转为后缀表达式。

这里需要用到动态数组和栈来进行转换

stack<char>ops;// 存储运算符
vector<char>sf;// 存储后缀表达式

而如何进行转换呢?有如下几点步骤

  1. 如果扫到数字,则直接存入后缀表达式,即sf
  2. 如果扫到左边括号,即(,则将左边括号存入运算符栈,即ops
  3. 如果扫到右边括号,即),则需先将运算符栈内的运算符不断存入到后缀表达式里并出栈,直到碰到左边括号为止,记得将左边括号出栈
  4. 如果扫到运算符,则需将运算符栈内的运算符不断存入后缀表达式里,直到碰到比当前运算符优先级低的运算符,或者栈为空,然后再将该运算符入栈
  5. 结束转换时,记得将运算符栈内剩余的运算符存入后缀表达式并出栈
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. 碰到叶子节点直接返回值
  2. 碰到非叶子节点时,先遍历左子树的值
  3. 基于左子树的值和当前节点判断是否发生短路:
    • 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;
 }
posted @ 2023-02-26 22:58  RegentalBread92  阅读(425)  评论(0编辑  收藏  举报