// 侧边栏目录 // https://blog-static.cnblogs.com/files/douzujun/marvin.nav.my1502.css //目录导航 //生成目录索引列表

【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>

posted @ 2024-10-23 23:25  SSL_LMZ  阅读(67)  评论(0)    收藏  举报