NYOJ-35-表达式求值
来源:互联网 发布:mac high sierra好吗 编辑:程序博客网 时间:2024/04/28 11:58
描述
ACM队的mdd想做一个计算器,但是,他要做的不仅仅是一计算一个A+B的计算器,他想实现随便输入一个表达式都能求出它的值的计算器,现在请你帮助他来实现这个计算器吧。
比如输入:“1+2/4=”,程序就输出1.50(结果保留两位小数)
输入
第一行输入一个整数n,共有n组测试数据(n<10)。
每组测试数据只有一行,是一个长度不超过1000的字符串,表示这个运算式,每个运算式都是以“=”结束。这个表达式里只包含+-*/与小括号这几种符号。其中小括号可以嵌套使用。数据保证输入的操作数中不会出现负数。
数据保证除数不会为0
输出
每组都输出该组运算式的运算结果,输出结果保留两位小数。
样例输入
2
1.000+2/4=
((1+2)*5+1)/4=
样例输出
1.50
4.00
题目比较容易理解,就是实现四则运算。
说到四则运算,不得不提到逆波兰表示法,也就是后缀表示法。这种表示法不需要括号,对于9 + (3 - 1) x 3 + 10 ÷ 2用后缀表示法的样子则是:9 3 1 - 3 * + 10 2 / +。之所以叫后缀的原因是,所有的运算符号都是要在运算数字的后边出现,那么很容易的想到,我们平时写的式子,也就是第一个式子,是中缀表示法。
中缀表达式转后缀表达式规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或者优先级低于栈顶符号(乘除优先于加减)则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止,这里我们需要一个栈来实现,用于符号的进栈出栈。
9 + (3 - 1) x 3 + 10 ÷ 2(中缀表达式) >>> 9 3 1 - 3 * + 10 2 / +(后缀表达式)
后缀表达式使用规则:从左到右遍历后缀表达式的每一个数字和符号,遇到是数字就进栈,遇到是符号,就当处于栈顶的两个数字出栈进行运算,运算结果进栈,一直到最终获得结果。
这里存在两个过程,
1.将中缀表达式转化为后缀表达式(栈用来进出运算符号)。
2.将后缀表达式进行运算得到结果(栈用来进出运算的数字)。
代码如下:
/*不知道为啥一直WA,可是我试了很多数据都可以的,哎,头疼死了。暂且记下,来日再战,我需要静静。后边的两个代码均是AC代码。*/#include <stdio.h>#include <string.h>#include <stdlib.h>#define INF 10000000#define ADD INF + 1 //+#define SUB INF + 2 //-#define MUL INF + 3 //x#define DIV INF + 4 //÷char str[1005]; //原公式int len; //公式长度char symbol[1000]; //运算符号栈float suffix[1005]; //后缀式float answer[1000]; //运算栈//从s[*pc]开始获取一个浮点数int StrToInt(char s[], int * pc, float *pout){ char buf[100]; int i = 0; if(s[*pc]<'0' || s[*pc]>'9') return 1; else { while((s[*pc] >= '0' && s[*pc] <= '9') || s[*pc] == '.') { buf[i] = s[*pc]; (*pc)++; i++; } buf[i] = '\0'; *pout = (float)atof(buf); return 0; }}void swi(int *key, char sym){ switch (sym) { case '+': suffix[(*key)++] = ADD; break; case '-': suffix[(*key)++] = SUB; break; case '*': suffix[(*key)++] = MUL; break; case '/': suffix[(*key)++] = DIV; break; }}void perform(int suf, int *_key){ float a = answer[(*_key)--]; float b = answer[(*_key)]; float c; switch (suf) { case (INF + 1): c = a + b; break; case (INF + 2): c = b - a; break; case (INF + 3): c = a * b; break; default: c = b / a; break; } answer[*_key] = c;// printf("%.2f\n", answer[*_key]); return ;}int main(){ int T; scanf("%d", &T); while(T--) { scanf("%s", str); len = (int)strlen(str) - 1; int key = 0; int top = -1; float pout; //中缀式转后缀式 for (int i = 0; i < len; ) { if (!StrToInt(str, &i, &pout)) { suffix[key++] = pout; } else if (top != -1 && str[i] == ')') { while (symbol[top] != '(') { swi(&key, symbol[top--]); } i++; top--; } else if (top != -1 && (str[i] == '+' || str[i] == '-') && (symbol[top] == '*' || symbol[top] == '/')) { while (symbol[top] != '(' && top >= 0) { swi(&key, symbol[top--]); } symbol[++top] = str[i++]; } else if (top != -1 && (str[i] == '*' || str[i] == '/') && (symbol[top] == '*' || symbol[top] == '/')) { swi(&key, symbol[top]); symbol[top] = str[i++]; } else { symbol[++top] = str[i++]; } } while (top >= 0) { swi(&key, symbol[top--]); }// for (int i = 0; i < key; i++)// {// printf("%f ", suffix[i]);// }// printf("\n"); //后缀式运算 int _key = -1; for (int i = 0; i < key; i++) { if (suffix[i] < INF) { answer[++_key] = suffix[i];// printf("%.2f\n", answer[_key]); } else { perform((int)suffix[i], &_key); } } printf("%.2f\n", answer[0]); } return 0;}
另外还有两种方法,第一种是将字符和数据分别入两个栈,然后根据优先级的比较,对数据栈顶的两个元素进行出栈操作然后进栈。
#include<stdio.h>#include<stdlib.h>//数据栈typedef struct DA{ float data[1000]; int pop;} SDA;//运算符栈typedef struct OP{ char op[1000]; int pop;} SOP;//初始化数据栈int InitSDA(SDA * p){ p->pop = 0; return 0;}//初始化运算符栈int InitSOP(SOP * p){ p->pop = 0; (p->op[p->pop]) = '='; (p->pop)++; return 0;}//数据入栈int PushSDA(SDA * p, float d){ if(p->pop < 1000) { p->data[p->pop] = d; (p->pop)++; return 0; } else return 1; //栈满}//运算符入栈int PushSOP(SOP * p, char c){ if(p->pop < 1000) { p->op[p->pop] = c; (p->pop)++; return 0; } else return 1; //栈满}//数据出栈int PopSDA(SDA * p, float * d){ (p->pop)--; if(p->pop >= 0) { *d = p->data[p->pop]; return 0; } else return 1;}//运算符出栈int PopSOP(SOP * p, char * c){ (p->pop)--; if(p->pop >= 0) { *c = p->op[p->pop]; return 0; } else return 1;}//从s[*pc]开始获取一个浮点数int StrToInt(char s[], int * pc, float *pout){ char buf[100]; int i = 0; if(s[*pc]<'0' || s[*pc]>'9') return 1; else { while((s[*pc] >= '0' && s[*pc] <= '9') || s[*pc] == '.') { buf[i] = s[*pc]; (*pc)++; i++; } buf[i] = '\0'; *pout = (float)atof(buf); return 0; }}//从s[*pc]获取一个charint StrToChar(char s[], int *pc, char *pout){ if('+'==s[*pc] || '-'==s[*pc] || '*'==s[*pc] || '/'==s[*pc] || '('==s[*pc] || ')'==s[*pc]) { *pout = s[*pc]; (*pc)++; return 0; } else return 1;}//获取优先级char GetPri(char c1, char c2){ char f[7][7] = {'>', '>', '<', '<', '<', '>', '>', '>', '>', '<', '<', '<', '>', '>', '>', '>', '>', '>', '<', '>', '>', '>', '>', '>', '>', '<', '>', '>', '<', '<', '<', '<', '<', '=', '\0', '>', '>', '>', '>', '\0', '>', '>', '<', '<', '<', '<', '<', '\0', '=',}; int i=0, j=0; switch(c1) { case '+': i = 0; break; case '-': i = 1; break; case '*': i = 2; break; case '/': i = 3; break; case '(': i = 4; break; case ')': i = 5; break; case '=': i = 6; break; } switch(c2) { case '+': j = 0; break; case '-': j = 1; break; case '*': j = 2; break; case '/': j = 3; break; case '(': j = 4; break; case ')': j = 5; break; case '=': j = 6; break; } return f[i][j];}//计算表达式float Operate(float a, char op, float b){ switch(op) { case '+': return a + b; case '-': return a - b; case '*': return a * b; case '/': return a / b; default: return 0; }}int main(void){ char s[10][1000]; int c = 0; float bufda; char bufop; float a, b; SDA sda; SOP sop; int n; int i; scanf("%d", &n); for(i = 0; i < n; i++) scanf("%s", s[i]); for(i = 0; i < n; i++) { c = 0; InitSDA(&sda); //初始化数据栈 InitSOP(&sop); //初始化符号栈 while(s[i][c] != '=' || sop.op[sop.pop - 1] != '=') // 计算未完成 { if(0 == StrToInt(s[i], &c, &bufda)) PushSDA(&sda, bufda); //数据入栈 else { switch(GetPri(sop.op[sop.pop - 1], s[i][c])) { case '<': if(0 == StrToChar(s[i], &c, &bufop)) PushSOP(&sop, bufop); break; case '=': PopSOP(&sop, &bufop); c++; break; case '>': PopSOP(&sop, &bufop); PopSDA(&sda, &b); PopSDA(&sda, &a); PushSDA(&sda, Operate(a, bufop, b)); break; } } } PopSDA(&sda, &a); printf("%.2f\n", a); } return 0;}//改写成C++,并简化后如此//#include <stack>//#include <stdio.h>//#include <ctype.h>//#include <string.h>//#include <stdlib.h>////using namespace std;////int priority(char c)//{// if(c == '=') return 0;// if(c == '+') return 1;// if(c == '-') return 1;// if(c == '*') return 2;// if(c == '/') return 2;// return 0;//}////void compute(stack<double>& Num,stack<char>& Op)//{// double b = Num.top();// Num.pop();// double a = Num.top();// Num.pop();// switch(Op.top())// {// case '+':Num.push(a+b);break;// case '-':Num.push(a-b);break;// case '*':Num.push(a*b);break;// case '/':Num.push(a/b);break;// }// Op.pop();//}////int main()//{// int z;// char str[1005];// stack<double> Num;// stack<char> Op;// scanf("%d",&z);// while(z--)// {// scanf("%s",str);// int len = strlen(str);// for(int i=0;i<len;i++)// {// if(isdigit(str[i]))// {// double n = atof(&str[i]);// while(i<len && (isdigit(str[i]) || str[i]=='.'))// i++;// i--;// Num.push(n);// }// else// {// if(str[i] == '(')// Op.push(str[i]);// else if(str[i] == ')')// {// while(Op.top()!='(')// compute(Num,Op);// Op.pop();// }// else if(Op.empty() || priority(str[i])>priority(Op.top()))// Op.push(str[i]);// else// {// while(!Op.empty() && priority(str[i])<=priority(Op.top()))// compute(Num,Op);// Op.push(str[i]);// }// }// }// Op.pop();// printf("%.2f\n",Num.top());// Num.pop();// }// return 0;//}
第二种是动态规划,将一个大问题切割成些许小问题,和归并排序的思想相仿。
// AC(动态规划)#include<stdio.h>#include<string.h>int len;int fst[1005];char str[1005];double Jud(int begin, int end); /*计算并返回表达式在区间[begin end]中的值*/int main(){ int T, i; double ans; scanf("%d", &T); while(T--) { memset(fst, 0, sizeof(fst)); /*一定要清0*/ scanf("%s", str); len = (int)strlen(str)-1; fst[0] = 1; for(i = 1; i <= len - 1; i++) /*fst[i]表示优先级,fst[i]越大,说明优先级越高↓↓*/ { /*例如str[] -- ((1+2)*5+1)/4=*/ if(str[i - 1]== '(') /*对应fst[] -- 12333222221110*/ fst[i] = fst[i - 1] + 1; else if(str[i] == ')') fst[i] = fst[i - 1] - 1; else fst[i] = fst[i - 1]; } ans = Jud(0, len - 1); /*传入整个表达式,不包括=*/ printf("%.2f\n", ans); } return 0;}double Jud(int begin, int end){ /*规定区间[begin, end]的优先级标准为fst[begin]*/ int i; double k; for(i = begin; i <= end; i++) /*先从做左到右找到第一个处于指定优先级的'+'运算符*/ { if(str[i]== '+' && fst[i] == fst[begin]) { k = Jud(begin, i - 1) + Jud(i + 1, end); /*将其拆成两个个表达式的和*/ return k; } } for(i = end; i >= begin; i--) /*如果找不到'+',再从右往左找到第一个处于指定优先级的'-'运算符*/ { if(str[i]=='-' && fst[i] == fst[begin]) { k = Jud(begin, i - 1) - Jud(i + 1, end); /*将其拆成两个个表达式的差*/ return k; } } for(i = begin; i <= end; i++) /*如果还找不到,再从左往右找到第一个处于指定优先级的'*'运算符*/ { if(str[i] == '*' && fst[i] == fst[begin]) { k = Jud(begin, i - 1) * Jud(i + 1, end); /*将其拆成两个个表达式的积*/ return k; } } for(i = end; i >= begin; i--) /*同上,从右往左找到第一个处于指定优先级的'/'运算符*/ { if(str[i] == '/' && fst[i] == fst[begin]) { k = Jud(begin, i - 1) / Jud(i + 1, end); /*将其拆成两个个表达式的商*/ return k; } } if(str[begin]=='(') /*如果在这个[begin,end]区间里的指定优先级中没有任何运算符,说明此区间可能完全包含上一级*/ { for(i = begin + 1; fst[i] >= fst[begin + 1]; i++); k = Jud(begin + 1, i - 1); } else /*既然没有包含上一级,说明这个区间就只剩下一个数啦*/ { char *p = str; sscanf(p+begin, "%lf", &k); /*将这个数赋值给k,并返回*/ } return k;}
理论上这三种均可以AC…可是我不知道第一种哪里出了BUG,等回头有空了再看吧…就这样吧。
- NYOJ-35-表达式求值
- NYOJ 35 表达式求值
- NYOJ 35表达式求值
- NYOJ-35-表达式求值
- nyoj 35 表达式求值
- NYOJ 35 表达式求值
- NYOJ 35 表达式求值
- NYOJ 35-表达式求值
- nyoj 35 表达式求值
- NYOJ 35表达式求值
- NYOJ-35 表达式求值
- NYOJ 35 表达式求值
- NYOJ 35 表达式求值
- nyoj 35 表达式求值
- NYOJ - 35 表达式求值
- NYOJ 35 表达式求值
- NYOJ 35 表达式求值
- NYOJ-35-表达式求值
- PAT 1004 Counting Leaves
- 1770 数数字
- 深入浅出RxJava三--响应式的好处
- 深入浅出RxJava四-在Android中使用响应式编程
- Javascript 读取浏览器名及版本号
- NYOJ-35-表达式求值
- Java 中日期格式的转化
- css3渐变、倒影、过渡 20160526
- Doctype
- PAT 1005 Spell It Right
- WebForms UnobtrusiveValidationMode 需要“jquery”ScriptResourceMapping。请添加一个名为 jquery (区分大小写)的 ScriptRes
- 利用YCSB工具对mongodb进行压测
- Fragment 实现android项目主流APP Tab (微博,今日头条等等),解决横竖屏切换重叠,以及切换回调。
- python eventlet模块