当前位置: 首页 > news >正文

CSP-J 2022_第三题逻辑表达式

题目

逻辑表达式是计算机科学中的重要概念和工具,包含逻辑值、逻辑运算、逻辑运算优先级等内容。
在一个逻辑表达式中,元素的值只有两种可能:0(表示假)和 1(表示真)。元素之间有多种可能的逻辑运算,本题中只需考虑如下两种:“与”(符号为 &)和“或”(符号为 |)。其运算规则如下:
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)
输出 #1
1
1 2
输入 #2
(0|1&0|1|1|(1|1))&(0&1&(1|0)|0|1|0)&0
输出 #2
0
2 3
说明/提示
【样例解释 #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
【数据范围】

设 ∣s∣ 为字符串 s 的长度。

对于所有数据,1≤∣s∣≤10
6
。保证 s 中仅含有字符 0、1、&、|、(、) 且是一个符合规范的逻辑表达式。保证输入字符串的开头、中间和结尾均无额外的空格。保证 s 中没有重复的括号嵌套(即没有形如 ((a)) 形式的子串,其中 a 是符合规范的逻辑表达式)。
在这里插入图片描述
其中:
特殊性质 1 为:保证 s 中没有字符 &。
特殊性质 2 为:保证 s 中没有字符 |。
特殊性质 3 为:保证 s 中没有字符 ( 和 )。

【提示】

以下给出一个“符合规范的逻辑表达式”的形式化定义:

字符串 0 和 1 是符合规范的;
如果字符串 s 是符合规范的,且 s 不是形如 (t) 的字符串(其中 t 是符合规范的),那么字符串 (s) 也是符合规范的;
如果字符串 a 和 b 均是符合规范的,那么字符串 a&b、a|b 均是符合规范的;
所有符合规范的逻辑表达式均可由以上方法生成。

代码

#include <bits/stdc++.h>//万能头文件,包含c++标准库中常用头文件 
using namespace std;//使用std命名空间,使用标准库中类型、函数时无需std::前缀 
int n_yu,n_huo; //与和或运算短路次数 
struct node{//二叉树节点类型 char x;//操作符号'&'、'|'、'('、')' 、'0'、'1' bool k;//各逻辑运算结果 node *fa, *left, *right;//指向父、左、右节点的指针 node() : x('*'),fa(nullptr), left(nullptr), right(nullptr) {}//默认构造函数,初始化节点 node(char cx){//带参构造函数,根据字符初始化节点 x=cx,fa=nullptr, left=nullptr, right=nullptr;if(cx=='1'||cx=='0')k=cx-'0';}
};
class Tree{public:node *root,*current;    // 根节点指针,当前操作节点指针(构建树时用)// 构造函数:初始化根节点和当前节点Tree(){//构造函数,构造根节点为'r'(不变),当前节点指向根 root=new node('r'), current=root;}~Tree() {// 析构函数:递归释放整个语法树节点内存clear(root);root = current = nullptr;}void insert(char x) {//根据字符类型,插入并构建语法树     node* newNode = new node(x);// 创建新节点if(x=='0'||x=='1'||x=='('){//操作数和左括号 newNode->fa = current;//成为当前节点的子节点 if(!current->left)current->left = newNode;//当前节点左子节点空,就挂到左节点 else if(!current->right)current->right = newNode;//否则挂右节点 }else if(x==')'){//右括号时要归拢,调整当前位置回到左括号处 current=current->fa;//下句无法越过当前的左括号,添加该句主动升一层 while(current->x!='(')current=current->fa;//循环一直找到左括号位置 newNode->fa=current;current->right=newNode;//左括号节点的右节点是右括号 newNode=newNode->fa;//左括号调整为当前节点位置 }else if(x=='|'){//优先级在|和&下,当前位置升到紧邻左括号或者根前 while(current->fa->x!='('&&current->fa!=root)current=current->fa;current->fa->left=newNode;newNode->fa=current->fa;//|变成当前节点父节点的左子, current->fa=newNode;newNode->left=current;//当前节点变成|的左子 }else if(x=='&'){//如果父节点是|,则&优先级高,要下潜。否则遇到&就得上浮 if(current->fa->x=='|'){current->fa->right=newNode;newNode->fa=current->fa;//当前节点的父节点变成&的父节点, current->fa=newNode;newNode->left=current;//当前节点变成&节点的左节点 }else{while(current->fa->x=='&')current=current->fa;//父是&就得上浮,得父不是&得节点为当前节点 if(current->fa->left==current)current->fa->left=newNode;//当前节点是父节点的左子,挂父节点的左树 else current->fa->right=newNode;//挂父节点的右树 newNode->fa=current->fa;current->fa=newNode;newNode->left=current;//当前节点(&)挂插入节点的左树 }}current=newNode;//插入的节点变成当前节点(括号选左括号) }void clear(node* x) {// 递归清除节点及其子节点if (!x) return;clear(x->left);clear(x->right);delete x;}void view1(node *x){cout<<x->x;if(x->left)view1(x->left);if(x->right)view1(x->right);}void view3(node *x){if(x->left)view3(x->left);cout<<x->x;if(x->right)view3(x->right);}void view2(node *x){cout<<x->x<<"["<<x->k<<"] ";if(x->left)view2(x->left);if(x->right)view2(x->right);}bool run(node *n){bool k1,k2; //左右树运算结果 if(n->x=='r')return n->k=run(n->left);//根和左括号处结果仅源自左树 if(n->x=='(')return n->k=run(n->left);//右括号不影响结果 if(n->x=='0'||n->x=='1')return n->k;//运算数 if(n->x=='&'){//与运算 k1=run(n->left);//先算左树 if(k1==0){//如果左树是0,直接返回0,不用算右树 n_yu++;return n->k=0;}k2=run(n->right);return n->k=k1&&k2;}if(n->x=='|'){//或运算 k1=run(n->left);if(k1==1){//如果左树是1,直接返回1,不用算右树 n_huo++;return n->k=1;}k2=run(n->right);return n->k=k1||k2;}}
}tree;
int main(){//freopen("expr.in","r",stdin);string s;cin>>s;for(int i=0;s[i];i++){tree.insert(s[i]); //cout<<i<<" "<<s[i]<<"先序";tree.view1(tree.root);cout<<endl;}//cout<<"先序";tree.view1(tree.root);cout<<endl;//cout<<"中序";tree.view3(tree.root);cout<<endl;cout<<tree.run(tree.root)<<endl;cout<<n_yu<<" "<<n_huo<<endl;//cout<<"先序";tree.view2(tree.root);cout<<endl;return 0;
}

思路

1.构建二叉树,n个字符n次操作,)、|和&需要上溯,最差情况是从当前节点回溯到根节点(n次),所以时间复杂度是o(n^2),这里n=10 ^6,结果是10 ^12,大于1秒的大约次数10 ^8次。有可能会超时——还好没有。
遍历逻辑表达式,每个字符建立一个指针空间节点
将所有节点连接成二叉树
经过一些样例数据的实操,得到逻辑关系
root
开始就有,用r标记,位置不变。后继只有左没有右
0、1、(
添(节点,或左或右
)
上溯,找到(节点,成为(节点的右节点,父(成当前节点。
先回溯一次,避免连续右括号时停留在当前(节点
|
添节点
上溯,直到父是根r或(,当前节点变成自己的左节点,自己成父的右节点
&
添节点
父是|,则当前节点(0、1)成为自己的左节点,自己成父(|)的右节点
父是&,则上溯直到父是|或者根或(,当前节点成左节点,自己成父的右节点
2.递归计算。run函数,,先左右再根,后序遍历,至多每个节点算一次O(n)。
return k=run(left)&&run(right);感觉只算左(不知道原因)
须要分别计算
k1=run(left)
k2=run(right)
&运算,左是0是短路。|运算左是1是短路。(当然另一边也存在这种情况,但是本题说了只是左)
有10^6个节点

作图验证

在这里插入图片描述

小结

冬麦需要9个月,夏玉米需要4个月。万事万物的生长成熟都需要足够的时间!

http://www.lryc.cn/news/603050.html

相关文章:

  • 技术工具箱 |五、一个避免头文件重复引用的 Python 脚本
  • 嵌入式Linux:注册线程清理处理函数
  • Zynq SOC FPGA嵌入式裸机设计和开发教程自学笔记:硬件编程原理、基于SDK库函数编程、软件固化
  • 第五章:进入Redis的Hash核心
  • 设计模式实战:自定义SpringIOC(亲手实践)
  • 深度研究——OpenAI Researcher Agent(使用OpenAI Agents SDK)
  • EAP(基于事件的异步编程模式)
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘papermill’问题
  • 时间数字转换器TDC的FPGA方案及核心代码
  • 将 NI Ettus USRP X410 的文件系统恢复出厂设置
  • C#:基于 EF Core Expression 的高性能动态查询构建实战 —— 面向大数据量环境的查询优化方案(全是干货,建议收藏)
  • Day22-二叉树的迭代遍历
  • 代码随想录Day32:动态规划(斐波那契数、爬楼梯、使用最小花费爬楼梯)
  • 10:00开始面试,10:06就出来了,问的问题有点变态。。。
  • Jmeter 性能测试监控之ServerAgent
  • AT89C 系列单片机知识点总结
  • 基于VHDL的神经网络加速器设计实战
  • 基于亮数据 MCP 的 Trae 智能体,让规模化 Google 数据实时分析触手可及
  • DBAPI的SQL实现模糊查询的3种方案
  • git相关操作记录
  • C++初学者4——标准数据类型
  • Day 24:元组与os模块
  • STM32F4—电源管理器
  • 新华三H3CNE网络工程师认证—Telnet
  • 在 CentOS 中安装 MySQL 的过程与问题解决方案
  • 每日面试题16:什么是双亲委派模型
  • LINUX 728 SHELL:grep;sort;diff
  • mp核心功能
  • CDN架构全景图
  • 【JavaScript】箭头函数和普通函数的区别