想了想,似乎有必要把自己刷OJ的历程记录下来。
不光光是为了总结报告和以后积累经验。
http://acm.sjtu.edu.cn/OnlineJudge/
上海交大的OJ网址。
-----------------------------------------------------------------------------我是华丽的分割线--------------------------------------------------------------------------------------------------------------------------------------
Description
二哥想自己做一个计算器,但是他需要一个程序来计算输入表达式的结果。你能帮助他吗?
Input Format
输入仅一行,给出一个算数表达式。表达式中包含:小括号,加减乘除,指数运算符,负号,整数,空格。其中负号的优先级最高(-),其次是指数运算(^),然后是乘除(*/),最后是加减(+-)。
这里规定除法为整除运算,如 5 / 2 = 2, 8 / -3 = -2 等等,与C++中的整除一致。另外注意指数运算为右结合,即 2^3^2 = 2^9 = 512 而非 2^3^2 = 8^2 = 64 。
输入的字符串长度不超过100。
Output Format
如果输入的表达式出现括号不匹配或者除零错误,输出一行“Error”(不含引号),否则输出运算结果。输入保证不包含任何其它类型的错误。
输入的数,输出的答案,以及中间结果均保证是不超过 long long 范围的整数。
一道计算器题。
有两种实现的思路。
1.文法树。
2.逆波兰表达式。
一开始我用的文法树的方式来实现。发现无论文法树还是逆波兰表达式效率其实都差不多。(显然文法树蛋疼很多很多)
但一直没搞明白关于逆波兰表达式的负号和减号的预处理。
参见:http://blog.csdn.net/jxsfreedom/article/details/4978189 最后评论里面没说清楚。。。
我用的primary,cache,term,expression 分别处理各个优先级。
代码:
//文法的规则了。
#include <iostream>
#include <cstdlib>
//#include <string>
#include<math.h>
using namespace std;
long long expression();
long long cache();
long long term();
long long primary();
void calculator();
void _exp(long long & x, long long y);
const char number = '3'; // t.kind==number means that t is a number Token
class Token
{
public:
char kind;
long long value;
Token(char ch):kind(ch){}
Token(char ch, long long v):kind(ch),value(v){}
};
class Token_stream
{
public:
Token_stream():full(false),buffer(0){}
Token get();
void putback(Token);
private:
bool full;
Token buffer;
}ts;
void Token_stream::putback(Token t)
{
full = true;
buffer = t;
}
Token Token_stream::get()
{
if(full)
{
full = false;
return buffer;
}
char ch;
ch=cin.get();
while (ch == ' ')ch = cin.get();
switch(ch)
{
case '\n':
case '(':
case ')':
case '+':
case '-':
case '*':
case '/':
case '^':
return Token(ch);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': // numeric literal
{
}
}
}
void calculator()
{
cout<< expression() << endl;
}
long long expression()
{
long long left = term();
//cout << "expre 1 : " << left << endl;
Token t = ts.get();
while(1)
{
switch(t.kind)
{
case '+':
t = ts.get();
break;
case '-':
left -= term();
t = ts.get();
break;
default:
{
if( t.kind == ')' || t.kind == '\n' )
{
//cout << "expre 2 : " << left << endl;
return left;
}
else {
cout << "Error" << endl;
exit(0);
}
}
}
}
}
long long term()
{
long long left = cache();
//cout << "term1 left is:" << left << endl;
while(1)
{
switch(t.kind)
{
case '*' :
left *= cache();
t = ts.get();
break;
case '/':
{
long long denominator = cache();
left /= denominator;
t = ts.get();
break;
}
default:
ts.putback(t);
//cout << "term2 left is:" << left << endl;
return left;
}
}
}
long long cache()
{
long long left = primary();
//cout << "!!!" << endl;
Token t = ts.get();
//cout << t.kind << "asfafa" << endl;
switch (t.kind)
{
case'^':
//cout << "cache1 left is:" << left << endl;
return left;
default:
ts.putback(t);
//cout << "cache2 left is:" << left << endl;
return left;
}
}
long long primary()
{
Token t = ts.get();
switch(t.kind)
{
case number:
{
//cout << "pri left is:" << t.value << endl;
return t.value;
}
case '(':
{
long long val = expression();
t = ts.get();
if (t.kind != ')') {
cout << "Error" << endl;
exit(0);
}
return val;
}
case '+':
return primary();
default:
cout << "Error" << endl;
exit(0);
}
}
void _exp(long long & x, long long y)
{
if (y == 0) x = 1;
if (x == 1) return;
if (x == 0)return;
long long r = 1, base = x;
while (y != 0)
{
if (y & 1)r *= base;
base *= base;
y >>= 1;
}
x = r;
}
int main()
{
calculator();
//cin.get();
return 0;
}
//为防止抄袭,代码有些部分被我删掉了。但大体框架还是可以看得很清楚的。需要源码的联系我。
而3^5^2是按照从右至左来执行。所以只能用递归了。
不管怎么说,总体设计还是挺精妙的。
最后交了好几次TLE了好几次。原因求幂算法复杂度太高。
换了快速幂就ac了.