题目传送

题的难点:1、有运算优先级,不好判断。2、有破坏整体和谐性的讨厌的括号。3、不知道哪里要填数。4、要求方案数很大,搜索不会做呐。

发现难点1和2都是中缀表达式的缺点。转成后缀表达式后难点1、2就烟消云散了。

普及一下:

  前缀表达式(又称波兰表达式)与后缀表达式(又称逆波兰表达式)较我们平常使用的中缀表达式,最主要的特点是没有括号。前/后缀表达式是一种十分有用的表达式,将中缀表达式转换为前缀表达式后,就可以只依靠出栈、入栈两种简单操作完全解决中缀表达式的全部运算。而平常我们一般都用后缀表达式(顺序从前到后,而前缀表达式的扫描顺序是从后向前)。

  计算后缀表达式,只要我们从前到后扫描,遇到数就入栈,遇到运算符就出栈两个数进行相应运算,最终弄结果再入栈。

  这里给出一个中缀表达式转后缀表达式的方法(建树什么的就别提了):

  设用一个结果栈和一个符号栈。从前向后扫描中缀表达式:

  1、若遇到数,直接入结果栈;

  2、若遇到运算符,弹出符号栈栈顶连续的优先级高于它的运算符,让那些运算符按弹出顺序依次入结果栈后再自己入符号栈;

  3、若遇到左括号,直接入符号栈;

  4、若遇到右括号,一直弹出符号栈至第一次弹出左括号,将弹出的运算符(不包括括号)按弹出顺序依次入结果栈;

  5、若扫描完中缀表达式后符号栈栈非空,则将符号栈元素全部弹出,将弹出的运算符按弹出顺序依次入结果栈。

  最后结果栈存的就是转换完的后缀表达式。计算时只要从结果栈的栈底开始扫,按照后缀表达式的计算过程计算即可。

再考虑从哪里填数。发现当我们无视括号时,剩下运算符的前后都会有一个数、相邻两运算符间也会有一个数。加上括号后只会改变运算顺序,不会改变数的数量。考虑起始时在结果栈先入一个数,每次遇到加号或乘号后再入一个数。辩证正确性:如果该加号(或乘号)后面有括号时:若为左括号,由于左括号直接入符号栈,所以入左括号进符号栈和入数进结果栈这两步操作的顺序无关紧要;若为右括号,那么该运算符后面就应该有一个数。如果该加号(或乘号)后面没有括号时,后面就该有一个数。辩证结束。

由于题目中要求的是最终表达式值为0的方案数,发现可由每次运算结果为0的方案数和结果为1的方案数推出: 设某运算符左边为1的方案数有l1种、为零的方案数有l0种;右边为1的方案数有r1种,右边为0的方案数有r0种。若该运算符为+,则结果为1的方案数则有l1*r1+l0*r1+l1*r2种,结果为0的方案数则有l0*r0种;若该运算符为*,则结果为1的方案数则有l1*r1种,结果为0的方案数则有l1*r0+l0*r0+l0*r1种。由此可以增设两个栈zero、one,分别维护随着结果栈进行运算时运算符左右两边为0的方案数和为1的方案数。思路就到此为止了。

见AC代码:

 #include<iostream>
#include<algorithm>
#include<cstdio>
#include<stack> using namespace std; int lenl; const int mod=; char exp[];//一开始输入的中缀表达式 string pol="n";//用'n'代替要填的数。pol为结果栈,但发现用string更好维护,于是就换成了string型。 stack<char> sta;//符号栈 stack<int>zero,one,ope;//zero、one的意义见上文,ope为计算后缀表达式时用到的辅助栈 int main()
{
cin>>lenl;
scanf("%s",exp);
for(int k=;k<lenl;k++)//中缀表达式转后缀表达式
{
if(exp[k]=='*'||exp[k]=='(')
sta.push(exp[k]);
if(exp[k]=='+')
{
while(!sta.empty()&&sta.top()=='*')
{
pol+='*';
sta.pop();
}
sta.push('+');
}
if(exp[k]==')')
{
while(sta.top()!='(')
{
pol+=sta.top();
sta.pop();
}
sta.pop();
}
if(exp[k]=='*'||exp[k]=='+') pol+='n';//填数
}
while(!sta.empty())//别忘了把符号栈剩余的元素转到结果里
{
pol+=sta.top();
sta.pop();
}
int l0,r0,l1,r1;
for(int k=;k<pol.length();k++)//计算后缀表达式
{
if(pol[k]=='n')
{
zero.push();
one.push();
}
if(pol[k]=='+')
{
l0=zero.top();zero.pop();
l1=one.top();one.top();
r0=zero.top();zero.pop();
r1=one.top();one.top();
zero.push(l0*r0%mod);
one.push(((l0*r1+l1*r0)%mod+l1*r1)%mod);
}
if(pol[k]=='*')
{
l0=zero.top();zero.pop();
l1=one.top();one.top();
r0=zero.top();zero.pop();
r1=one.top();one.top();
zero.push(((l0*r0+l1*r0)%mod+l0*r1)%mod);
one.push(l1*r1%mod);
}
}
cout<<zero.top();//最终使表达式值为零的方案数
return ;
}

做这个题时如果不会转中缀表达式转后缀表达式的话模拟也是可以骗点分的,但要注意考虑周全。(就算是一点微不足道的手误,也能造就90分到10分的神话(说多了都是泪))

洛谷P1310 表达式的值——题解的更多相关文章

  1. 2019.06.17课件:[洛谷P1310]表达式的值 题解

    P1310 表达式的值 题目描述 给你一个带括号的布尔表达式,其中+表示或操作|,*表示与操作&,先算*再算+.但是待操作的数字(布尔值)不输入. 求能使最终整个式子的值为0的方案数. 题外话 ...

  2. 洛谷P1310 表达式的值 题解 栈/后缀表达式的应用

    题目链接:https://www.luogu.org/problem/P1310 本题涉及算法:栈.前缀表达式转后缀表达式,动态规划思想. 这道题目我思考了好长时间,第一时间让我做的话我也做不出来. ...

  3. 洛谷 P1310 表达式的值 解题报告

    P1310 表达式的值 题目描述 对于1 位二进制变量定义两种运算: 运算的优先级是: 先计算括号内的,再计算括号外的. "× "运算优先于"⊕"运算,即计算表 ...

  4. 洛谷P1310 表达式的值

    P1310 表达式的值 题目描述 对于1 位二进制变量定义两种运算: 运算的优先级是: 先计算括号内的,再计算括号外的. “× ”运算优先于“⊕”运算,即计算表达式时,先计算× 运算,再计算⊕运算.例 ...

  5. 【洛谷P1310 表达式的值】

    题目链接 题目描述 对于1 位二进制变量定义两种运算: 运算的优先级是: 先计算括号内的,再计算括号外的. “× ”运算优先于“⊕”运算,即计算表达式时,先计算× 运算,再计算⊕运算.例如:计算表达式 ...

  6. 洛谷P1981 表达式求值 题解 栈/中缀转后缀

    题目链接:https://www.luogu.org/problem/P1981 这道题目就是一道简化的中缀转后缀,因为这里比较简单,只有加号(+)和乘号(*),所以我们只需要开一个存放数值的栈就可以 ...

  7. 洛谷 P1981 表达式求值

    P1981 表达式求值 题目描述 给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值. 输入输出格式 输入格式: 输入文件为 expr.in. 输入仅有一行,为需要你计算的表达式,表达式中只 ...

  8. 题解-洛谷P1981 表达式求值(模拟+处理优先级的递归)

    https://www.luogu.org/problemnew/show/P1981 (原题链接) 显然乘法的优先级高与加法,因此碰到乘号就要优先把一连串与乘号相连的数算出,很容易想到递归.可用普通 ...

  9. 洛谷 P5146 最大差值 题解

    P5146 最大差值 题目描述 HKE最近热衷于研究序列,有一次他发现了一个有趣的问题: 对于一个序列\(A_1,A_2\cdots A_n\)​,找出两个数\(i,j\),\(1\leq i< ...

随机推荐

  1. 简单记事本的基本实现&十四周总结

    JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口 ...

  2. 【监控笔记】【1.3】监控事件系列——SQL Trace(黑盒跟踪 BlackBox Trace)

    [1]它跟踪了哪些事件? (1.1)存储过程执行(SP:Strating) (1.2)T-SQL执行(SQL:BatchString) (1.3)错误和警告(Exception,Attention) ...

  3. P1079Vigenère密码

    这是2012年noip提高组的的DAY1T1,我用了一下午的时间,一次性AC^^. 这是一个字符串的模拟题.首先给出了一个密码对应法则,我们发现在同一对角线的明文通过密钥得出来的密文是相同的.根据八皇 ...

  4. Javascript的是三种字符串连接方式

    第一种:用连接符“+”连接字符串 str="a"; str+="b"; 这种方法相对以下两种,最便捷快速.建议100字符以下的连接使用这种连接方式. 第二种:以 ...

  5. Python入门之 函数

    Python入门之 函数 1.初识函数 1.1 什么是函数? <1> 将某个功能封装到一个空间中就是一个函数 <2> 减少重复代码 1.2 定义函数 def -- python ...

  6. 检验Excel中数据是否与数据库中数据重复

    #region 记录Excel中的重复列 /// <summary> /// 记录Excel中的重复列 /// </summary> /// <param name=&q ...

  7. C++设计模式:访客模式

    访客模式:通俗的说, 就是定义一个访问者角色, 当对指定角色进行访问时要通过访问者进行访问. 访客模式的侵入性适中,仅在被访问的类里面加一个对外提供接待访问者的接口. 访客模式的优点: 符合单一职责原 ...

  8. 声明对象的方式/构造函数/原型/this指向

      函数的发展历程(声明函数的方式):     1.通过Object构造函数或字面量的方式创建单个对象 var obj = new Object; obj.name="新华"; o ...

  9. JS 的 Array 和String 常混淆方法

    知识一: 1.slice() 提取字符串 slice[ start, end) 如果参数为负数,表示从尾部开始算起. 2.subString() 提取字符串 3.subStr() 提取字符串 subS ...

  10. /etc/nscd.conf - 域名服务缓存守护进程配置文件

    描述 DESCRIPTION 该文件 /etc/nscd.conf 在启动 nscd(8) 时读入.每一行或者指定一个属性和值,或者指定一个属性.服务和一个值.域之间通过空格或者TAB分开.‘#’表示 ...