【SSL 1955】【CSP/J 2022 T3】逻辑表达式(递归)
题目大意
逻辑表达式是计算机科学中的重要概念和工具,包含逻辑值、逻辑运算、逻辑运算优先级等内容。
在一个逻辑表达式中,元素的值只有两种可能:00(表示假)和 11(表示真)。元素之间有多种可能的 逻辑运算,本题中只需考虑如下两种:“与”(符号为 &)和“或”(符号为 |)。其运算规则如下:
0&0=0&1=1&0=0,1&1=1;
0|0=0 , 0∣1=1∣0=1∣1=1。
在一个逻辑表达式中还可能有括号。规定在运算时,括号内的部分先运算;两种运算并列时,& 运算优先于 | 运算;同种运算并列时,从左向右运算。
比如,表达式 0|1&0 的运算顺序等同于 0|(1&0);表达式 0&1&0|1 的运算顺序等同于 ((0&1)&0)|1。
此外,在 C++ 等语言的有些编译器中,对逻辑表达式的计算会采用一种“短路”的策略:在形如 a&b 的逻辑表达式中,会先计算 a 部分的值,如果 a=0,那么整个逻辑表达式的值就一定为 0,故无需再计算 b 部分的值;同理,在形如 a|b 的逻辑表达式中,会先计算 a 部分的值,如果 a=1,那么整个逻辑表达式的值就一定为 1,无需再计算 b 部分的值。
现在给你一个逻辑表达式,你需要计算出它的值,并且统计出在计算过程中,两种类型的“短路”各出现了多少次。需要注意的是,如果某处“短路”包含在更外层被“短路”的部分内则不被统计,如表达式 1|(0&1) 中,尽管 0&1 是一处“短路”,但由于外层的 1|(0&1) 本身就是一处“短路”,无需再计算 0&1 部分的值,因此不应当把这里的 0&1 计入一处“短路”。
输入格式
输入共一行,一个非空字符串 s 表示待计算的逻辑表达式。
输出格式
输出共两行,第一行输出一个字符 0 或 1,表示这个逻辑表达式的值;第二行输出两个非负整数,分别表示计算上述逻辑表达式的过程中,形如 a&b 和 a|b 的“短路”各出现了多少次。
输入样例
【输入样例1】
0&(1|0)|(1|1|1&0)
【输入样例2】
(0|1&0|1|1|(1|1))&(0&1&(1|0)|0|1|0)&0
输出样例
【输出样例1】
1
1 2
【样例解释 #1】
该逻辑表达式的计算过程如下,每一行的注释表示上一行计算的过程:
0&(1|0)|(1|1|1&0)
=(0&(1|0))|((1|1)|(1&0)) //用括号标明计算顺序
=0|((1|1)|(1&0)) //先计算最左侧的 &,是一次形如 a&b 的“短路”
=0|(1|(1&0)) //再计算中间的 |,是一次形如 a|b 的“短路”
=0|1 //再计算中间的 |,是一次形如 a|b 的“短路”
=1
【输出样例2】
0
2 3
基本思路
既然是有括号,必须分级处理。因为从最大括号向内部下一级处理,所以我们考虑用递归来解决这个问题。
每搜到一级括号,先计算其数字部分,遇到括号再向下一级递归。
如何处理括号分级的问题呢?我们设一个变量inf,遇到左括号++,遇到右括号--,便是它的级别了。
又如何划分计算区间(往下递归)呢?下面代码中的wand和wor数组记录的是离当前下标最近的分割符号,仔细看看便能理解。
核心代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int SIZE=1e6+10;
string num;
int ans1,ans2,n,p_or[SIZE],p_and[SIZE],wor[SIZE],wand[SIZE];
inline int dfs(int z,int y){
if(z==y) return num[z]-48;
int ap=wand[y],op=wor[y],inf=0;
//1&(1|1)
//对于7号位而言,若仅算括号区间,肯定不能找到左括号前去
if(op>0&&op>z){
int l=dfs(z,op-1);//搜下一级区间
if(l==1){
ans1++;
return 1;
}
int r=dfs(op+1,y);
return l|r;
}
if(ap>0&&ap>z){
int l=dfs(z,ap-1);
if(l==0){
ans2++;
return 0;
}
int r=dfs(ap+1,y);
return l&r;
}
if(num[z]=='('&&num[y]==')') return dfs(z+1,y-1);
//(...)|(...)
}
void getnum(){//预处理函数
int inf=0;
for(int i=1;i<=n;i++){
if(num[i]=='(') inf++;
else if(num[i]==')') inf--;
else if(num[i]=='|') p_or[inf]=i;
else if(num[i]=='&') p_and[inf]=i;//必须为同级括号
/*
0|1&0
对于最后一个0而言,或才是最近的分割符号
大概是这么一个逻辑,注意一下
*/
wand[i]=p_and[inf];
wor[i]=p_or[inf];
}
}
int main(){
ios::sync_with_stdio(false);
cin>>num;
num=' '+num;
n=num.length()-1;
getnum();
cout<<dfs(1,n)<<endl;
cout<<ans2<<" "<<ans1;
return 0;
}
<details>
<summary>点击查看代码</summary>

浙公网安备 33010602011771号