主要包括一些中缀、后缀表达式值的计算、表达式是否合法的判断
后缀表达式的讲解:
人工怎么写出中缀表达式的后缀表达式
计算机实现方法:
NOIP and CSP真题
2022 逻辑表达式
这个其实也是在讨论&和|的无用情况,但是要记录整个式子的无用计算次数,所以这里用stack和pair两种结构,自底向上统计无用情况
题解:https://www.cnblogs.com/shirlybaby/p/17803059.html
2020 表达式 http://ybt.ssoier.cn:8088/problem_show.php?pid=2006
这个题要求的是每个元素对总体结果的改变情况,因为在&和|运算中,有的元素值的不同对总体结果没有影响,所以这道题的思路就是建立表达式树,
然后对树上每个元素打上标记,看是否是一个无用的,在打标机的过程的过程中用DFS,而且还要标记下传
题解:https://www.cnblogs.com/shirlybaby/p/17803737.html
1356:计算(calc)----中缀
表达式求值:这里的表达式就是最正常的表达式,有加减乘除,括号等
第一种方法:直接计算,不用转为后缀表达式,边弄边算
#include<iostream> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<cmath> #include<ctime> #include<algorithm> #include<utility> #include<stack> #include<queue> using namespace std; //表达式求值,这里的表达式就是最正常的表达式,有加减乘除,括号等 //用一个函数来判断运算符的优先级 int n,m; int lev(char x)//运算符优先级 { if(x=='+'||x=='-')//加减的优先级很低 return 1; if(x=='*'||x=='/') return 2; if(x=='^') return 3; return 0;//括号的优先级很低 } void calculate(stack<int> &s1,stack<char> &s2)//弹出栈顶元素并计算//参数是一个运算数的栈和一个运算符的栈 { /*取出后弹出栈*/ int y=s1.top(); s1.pop(); int x=s1.top(); s1.pop();//弹出两个操作数 char z=s2.top(); s2.pop();//弹出一个操作符 /*根据运算符计算,并压入栈*/ if(z=='+') s1.push(x+y); if(z=='-') s1.push(x-y);//这些也要注意是谁加谁是谁减谁 if(z=='*') s1.push(x*y); if(z=='/') s1.push(x/y); if(z=='^') s1.push(pow(x,y));//注意谁是底数,谁是指数 } char str[1000000]; int main(){ scanf("%s",str+1); n=strlen(str+1); stack<int> s1; stack<char> s2; int temp=0; bool flag=false;//这两个参数都是用来临时保存temp和标记是否是数flag的 for(int i=1;i<=n;i++) { if('0'<=str[i]&&str[i]<='9')//判断当前字符是否为数字 { temp=(temp<<3)+(temp<<1)+str[i]-'0';//temp*=10+str[i]-'0' flag=true; } else {//操作数在这里入栈 if(flag) { s1.push(temp); temp=0; flag=false;//操作数入栈,数被temp临时保存,这里入栈后temp清零 } if(str[i]=='(') { s2.push(str[i]); continue; } if(str[i]==')') { while(s2.top()!='(')//注意这里要判断前一个是不是(,有这种可能性 calculate(s1,s2); s2.pop();//注意这里还要弹栈,因为有一个( continue; } while(!s2.empty()&&lev(s2.top())>=lev(str[i]))//优先级判断//运算符入栈 calculate(s1,s2);//如果运算符栈不空而且当前的运算符优先级较低,就先算了来(即先计算优先级高的) s2.push(str[i]);//运算符入栈 }//这一这里面就是一个if-else,分为是否是操作符和是否是操作数 } if(flag) { s1.push(temp); temp=0; flag=false; } while(!s2.empty()) calculate(s1,s2); cout<<s1.top()%10000<<endl; return 0; }
1358:中缀表达式值(expr)
没有括号,但是需要判断是不是合法的中缀表达式,而且-可能是负数符号(但感觉好像没管??)
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #define Int stack<int> #define Char stack<char> using namespace std; //这里的用法 Int s1;Char s2; inline int lev(char x){ if(x=='+'||x=='-') return 1; //用到很多的内联函数,为了节省栈空间 if(x=='*'||x=='/') return 2; if(x=='^') return 3; return 0;//数字或括号返回0 } //快速幂的迭代写法 inline int ksm(int s,int t){ int ans=1; while(t){ if(t&1) ans*=s;//如果t是奇数 s*=s; //乘以本身,是转化为2进制的写法 t>>=1;//t除以2 } return ans; } //弹栈并且计算 inline void js(Int &s1,Char &s2){ int y=s1.top();s1.pop(); int x=s1.top();s1.pop();//运算数 char c=s2.top();s2.pop();//运算符 if(c=='-') s1.push(x-y); if(c=='+') s1.push(x+y); if(c=='*') s1.push(x*y); if(c=='/') s1.push(x/y); if(c=='^') s1.push(ksm(x,y));//快速幂 } inline int c(int x){ return x!=0;//这个函数是用来判断表达式是否合法的,和判断优先级一起用的 } char sr[100001]; int sum[100001]; int main(){ scanf("%s",sr+1);//注意写法,是从1开始的 int n=strlen(sr+1)-1;//-1是因为去除末尾的@ for(int i=1;i<=n;i++){ sum[i]+=sum[i-1]; if(sr[i]=='(') ++sum[i]; if(sr[i]==')') --sum[i]; //这个for循环是用来匹配检查的,判断合法性,()是否匹配 } bool out=0;//判断是否存在*/ 等情况 for(int i=2;i<=n;i++){ if(c(lev(sr[i]))&&c(lev(sr[i-1]))){ out=1; break; } } if(n==1&&c(lev(sr[1]))||sum[n]||out){ cout<<"NO"<<endl;//不合法的三种情况 return 0; } //判断完了合法性后就和calc一样了,开始计算 Int s1; Char s2; int temp=0,flag=0; for(int i=1;i<=n;i++){ if(sr[i]>='0'&&sr[i]<='9'){ temp=(temp<<3)+(temp<<1)+sr[i]-'0';//注意前面的写法,等价于temp*10 flag=1; } else{ if(flag) { s1.push(temp); temp=0;flag=0; }//先判断是不是数 if(sr[i]=='('){ s2.push(sr[i]); continue;//推出本次循环 } if(sr[i]==')'){ while(s2.top()!='(') js(s1,s2); s2.pop();//然后就弹栈 continue;//退出本次循环 } while(!s2.empty()&&lev(sr[i])<=lev(s2.top())) js(s1,s2); s2.push(sr[i]);//运算符入栈 } } if(flag){ s1.push(temp); temp=flag=0; } while(!s2.empty()) js(s1,s2); cout<<s1.top(); return 0; }
1331:【例1-2】后缀表达式的值
在一本通上面不行了,会出现编译错误,好像是gets函数的原因,不懂!!!!
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<cstdio> using namespace std; //栈 char s[256]; int stack[267];//注意stack类型是int,不放运算符 void comp(char s[]){ int top=0,i=0; int x=0; while(i<=strlen(s)-2){//因为最后一个是@ switch(s[i]){ //计算后缀表达式:要用到switch case '+': stack[--top]+=stack[top+1];break; case '-': stack[--top]-=stack[top+1];break; case '*': stack[--top]*=stack[top+1];break; case '/': stack[--top]/=stack[top+1];break; default: x=0;while(s[i]!=' ') x=x*10+s[i++]-'0';//处理多位数 stack[++top]=x;break; } i++; }//年纪大了:要注意switch的写法 cout<<stack[top]<<endl; } int main(){ gets(s);//要用gets() comp(s); return 0; }
1358:中缀表达式值(expr)
中缀表达式的值,还带检查表达式合不合法,注意表达式不合法的三种情况:
1)n==1且第一个是运算符
2)括号不匹配
3)出现连续运算符
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #define Int stack<int> #define Char stack<char> using namespace std; //这道题既需要判断,也需要计算 //这里的用法 Int s1;Char s2; inline int lev(char x){ if(x=='+'||x=='-') return 1; //用到很多的内联函数,为了节省栈空间 if(x=='*'||x=='/') return 2; if(x=='^') return 3; return 0;//数字或括号返回0 } //快速幂的迭代写法 inline int ksm(int s,int t){ int ans=1; while(t){ if(t&1) ans*=s;//如果t是奇数 s*=s; //乘以本身,是转化为2进制的写法 t>>=1;//t除以2 } return ans; } //弹栈并且计算 inline void js(Int &s1,Char &s2){ int y=s1.top();s1.pop(); int x=s1.top();s1.pop();//运算数 char c=s2.top();s2.pop();//运算符 if(c=='-') s1.push(x-y); if(c=='+') s1.push(x+y); if(c=='*') s1.push(x*y); if(c=='/') s1.push(x/y); if(c=='^') s1.push(ksm(x,y));//快速幂 } inline int c(int x){ return x!=0;//这个函数是用来判断表达式是否合法的,和判断优先级一起用的 } char sr[100001]; int sum[100001]; int main(){ scanf("%s",sr+1);//注意写法,是从1开始的 int n=strlen(sr+1)-1;//-1是因为去除末尾的@ for(int i=1;i<=n;i++){ sum[i]+=sum[i-1]; if(sr[i]=='(') ++sum[i]; if(sr[i]==')') --sum[i]; //这个for循环是用来匹配检查的,判断合法性,()是否匹配 } bool out=0;//判断是否存在*/ 等情况 for(int i=2;i<=n;i++){ if(c(lev(sr[i]))&&c(lev(sr[i-1]))){ out=1; break; } } if(n==1&&c(lev(sr[1]))||sum[n]||out){ cout<<"NO"<<endl;//不合法的三种情况 //这个就很考验人了:1)n==1 2)第一个是运算符 3)括号不匹配 4)出现连续运算符 return 0; } //判断完了合法性后就和calc一样了,开始计算 Int s1; Char s2; int temp=0,flag=0; for(int i=1;i<=n;i++){ if(sr[i]>='0'&&sr[i]<='9'){ temp=(temp<<3)+(temp<<1)+sr[i]-'0';//注意前面的写法,等价于temp*10 flag=1; } else{ if(flag) { s1.push(temp); temp=0;flag=0; }//先判断是不是数 if(sr[i]=='('){ s2.push(sr[i]); continue;//推出本次循环 } if(sr[i]==')'){ while(s2.top()!='(') js(s1,s2); s2.pop();//然后就弹栈 continue;//退出本次循环 } while(!s2.empty()&&lev(sr[i])<=lev(s2.top())) js(s1,s2); s2.push(sr[i]);//运算符入栈 } } if(flag){ s1.push(temp); temp=flag=0; } while(!s2.empty()) js(s1,s2); cout<<s1.top(); return 0; }
1353:表达式括号匹配(stack)
#include<iostream> #include<cstring> #include<stack> #include<algorithm> #include<cstdio> using namespace std; string s; stack<char> ss; int main(){ cin>>s; for(int i=0;s[i]!='@';i++){ if(s[i]=='(') ss.push(s[i]); if(s[i]==')'){ if(!ss.empty()) ss.pop(); else { cout<<"NO"<<endl; return 0; } } } if(!ss.empty()) cout<<"NO"<<endl; else cout<<"YES"<<endl; return 0; }
1354:括弧匹配检验
括弧匹配检验、字符串匹配问题:思想:用数字来表示字符,可以找到正确的规律
char str[10000]; stack<int> S;//用1代表(,2代表),3代表[,4代表]//用数字来代表符号 int main(){ cin>>str; int len=strlen(str); for(int i=0;i<len;i++) { if(str[i]=='(')//记录左圆括号 S.push(1); else if(str[i]==')')//记录右圆括号 { if(S.empty())//栈为空 S.push(2); else if(S.top()==1) S.pop(); else S.push(2); } else if(str[i]=='[')//记录左方括号 S.push(3); else if(str[i]==']')//记录右方括号 { if(S.empty())//栈为空 S.push(4); else if(S.top()==3) S.pop(); else S.push(4); } } if(S.empty()) printf("OK"); else printf("Wrong"); return 0; }
1355:字符串匹配问题(strs)
int n; char s[256]; int a[256]; char b[256]={0}; void change(char s[]){ for(int i=0;i<strlen(s);i++){ if(s[i]=='{') a[i+1]=1; if(s[i]=='[') a[i+1]=2; if(s[i]=='(') a[i+1]=3; if(s[i]=='<') a[i+1]=4; if(s[i]=='>') a[i+1]=5; if(s[i]==')') a[i+1]=6; if(s[i]==']') a[i+1]=7; if(s[i]=='}') a[i+1]=8;//将较多的符号转化为数字来判断,找规律:加起来等于9的是合理的 } } int main(){ cin>>n; while(n--){ cin>>s; change(s); int t=0; for(int j=1;j<=strlen(s);j++){ if(a[j]<=4) if(a[j]>=b[t]) b[++t]=a[j]; else break; if(a[j]>=5) if(a[j]+b[t]==9) t--; else t++; } if(t==0) cout<<"YES"<<endl;//还是用栈中元素个数来判断正误 else cout<<"NO"<<endl; } return 0; }
1357:车厢调度(train)
就是模拟弹栈的时候进入的序列是否是合法的序列
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int n; int a[1001]; int stack[1001]; int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; int top=0;//记住有这个标志啊 for(int i=1,cur=1;i<=n;i++){ while(cur<=a[i]) stack[++top]=cur++; //第一种情况,需要进栈 if(stack[top]==a[i]) --top;//若在栈顶则弹出(第二种情况,等于) else { cout<<"NO"<<endl;//如果不在栈顶,就错误 (第三种情况) return 0; } } cout<<"YES"<<endl; return 0; }