OpenJudge NOI 3.3 3340:RPN Calculator

【题目链接】

OpenJudge NOI 3.3 3340:RPN Calculator

【题目翻译】

逆波兰表示法计算器

描述

逆波兰表示法与波兰表示法类似。波兰表示法是由波兰数学家扬·武卡谢维奇于1920年引入的,是一种每个操作符都在它的操作数后面的数学表示法,它也被称作:前缀表示法。
在逆波兰表示法中运算符在它们的操作数后面;例如,要表示三加四,一个人应该写“3 4 +”,而不是“3 + 4”。如果有多个运算符,运算符必须直接出现在它的第二个操作数后面。所以,用传统的中缀表示法写出的“3 - 4 + 5”在逆波兰表示法中写为“3 4 - 5 +”:先做3减4,再加5。逆波兰表示法的好处是它避免了在一些中缀表示法中必须写的括号。“3 - 4 * 5”也可以写为“3 - (4 * 5)”,它与“(3 - 4) * 5”的意义非常不同,只有使用括号才能区分二者的意义。在前缀表示法中,前面的一个表达式可以写为:“3 4 5 * -”,这也能无歧义第表示“3 (4 5 *)”
要求你设计一个简单的逆波兰表达式计算器,需要支持 + , − , ∗ , / +, -, *, / +,,,/,(除数的绝对值不小于 1 0 − 9 10^{-9} 109)与^(乘方运算符,如果底数 b ≤ 0 b \le 0 b0,那么指数 e e e一定是一个正数而且不大于 1 0 9 10^9 109
你可以认为运算中涉及到的所有的数字都可以由双精度浮点型变量表示。
另外,我们的计算器有一些“内存”。每次我们计算一个表达式,“内存”中最小的数字会被当前表达式的值替换掉。

输入:

第一行包含一个整数n,表示计算器的“内存”大小。
从第二行开始,我们会给出n个数字,为“内存”中的初始值。除了最后一行,每行会给出10个数字。
然后每行有一个合法的我们之前描述的逆波兰表达式,以“=”结束。“=”是计算的指令。每项不会超过20个字符。

输出:

对于每个表达式,在一行内输出它的值。
然后输出一个空行分隔两部分。
最后,以升序输出内存中的所有的数字,10个数字一行。
每个数字必须以科学计数法表示,整数小数点后保留6位,指数保留2位。(就像C语言中用printf("%e")格式化字符串一样)一行中的各个数字应该用空格分隔。

样例输入

4
1e6 1e-6 0.001 1000
1 2 + 3 4 + * =
1 0.1 / 8 ^ =

样例输出

2.100000e+01
1.000000e+08

2.100000e+01 1.000000e+03 1.000000e+06 1.000000e+08

提示

有大量输入,建议使用scanf()
%e格式输出在windows环境下指数部分为3位,在系统的测试环境下为2位。

【题目考点】

1. 表达式求值
2. 表达式树

表达式树:一棵表达式树可以表示一系列的运算。
表达式树中的结点包括运算符与数值

struct Node
{
	char c;//运算符
	int n;//数值
}
  • 分支结点:c:运算符,n:该子树对应的表达式的值
  • 叶子结点:c:'\0',n:数值
    表达式树的值,是左子树的值和右子树的值,经过根结点运算符运算后得到的结果。

【解题思路】

解法1:后缀表达式直接求值
  1. 从左向右扫描后缀表达式。
  2. 遇到数字时,将数字入栈。
  3. 遇到运算符时,弹出栈顶的两个数,用该运算符对它们做相应的计算,结果入栈。
  4. 扫描结束后,栈顶数字就是结果。
解法2:后缀表达式构造表达式树
  1. 如果读到数字,那么新建数字结点入栈。
  2. 如果读到运算符,则新建运算符结点np,出栈两个结点,将这两个结点的值通过np的运算符运算后得到的值存为新结点np的值,将np入栈。
  3. 读完整个后缀表达式,栈中应该只剩1个结点,该结点就是表达式树的根结点,该结点的值就是表达式的值 。
解法3:递归

先将字符串处理为由结构体对象构成的后缀表达式,每个结构体对象可能是数字可能是运算符。
后缀表达式的结构为:<第一个运算单元><第二个运算单元><运算符>
每个运算单元也许是一个数字,也许是一个后缀表达式。
后缀表达式数组最后一个元素的下标为p。
设函数solve(),求从第p位置开始向左遍历取到的第一个运算单元的值。每取一个元素,p向左移动一个位置。

  • 如果p位置是数字,直接返回这个数字的值。
  • 如果p位置是运算符,那么从p位置开始向左取两个运算单元的值,并用当前位置的运算符进行计算,得到这一运算单元的值。

【题解代码】

解法1:后缀表达式直接求值
#include <bits/stdc++.h>
using namespace std;
#define N 105
struct Node
{
    double n;
    char c;
};
Node eq[N];
int p;
priority_queue<double, vector<double>, greater<double> > pq;//小顶堆 
double calc(double a, double b, char c)
{
    switch(c)
    {
        case '+':
            return a+b;
        case '-':
            return a-b;
        case '*':
            return a*b;
        case '/':
            return a/b;
        case '^':
            return pow(a, b);
    }
}
double solve()//求解后缀表达式eq 
{
    stack<double> stk;
    for(int i = 1; i <= p; ++i)
    {
        if(eq[i].c == '\0')
            stk.push(eq[i].n);
        else
        {
            double b = stk.top();
            stk.pop();
            double a = stk.top();
            stk.pop();
            stk.push(calc(a, b, eq[i].c));
        }
    }
    return stk.top();
}
int main()
{
    char s[25];
    double a, ans; 
	int n, ct = 0;//ct:输出个数 
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i)
	{
        scanf("%lf", &a);
        pq.push(a);
    }
    while(scanf("%s", s) != EOF)
    {
        if(s[0] >= '0' && s[0] <= '9' || s[0] == '-' && s[1] >= '0' && s[1] <= '9')//如果s是数字(可能是负数) 
        {
            eq[++p].n = atof(s);//将字符串s转为浮点数。
            eq[p].c = '\0'; 
        }
        else if(strcmp(s, "=") == 0)//遇到等号,求解后缀表达式 
        {
            ans = solve();
            printf("%e\n", ans);
            pq.pop(); 
            pq.push(ans);//将求解的结果存入堆 
            p = 0;
        }
        else//是+-*/^ 
        {
            eq[++p].n = 0;
            eq[p].c = s[0];
        }
    }
    putchar('\n');
    while(pq.empty() == false)
    { 
        printf("%e ", pq.top());
        ct++;
        if(ct == 10)
        {
            putchar('\n');
            ct = 0;
        }
        pq.pop();
    }
	return 0;
}
解法2:递归
#include <bits/stdc++.h>
using namespace std;
#define N 105
struct Node
{
    double n;
    char c;
};
Node eq[N];
int p;
priority_queue<double, vector<double>, greater<double> > pq;//小顶堆 
double calc(double a, double b, char c)
{
    switch(c)
    {
        case '+':
            return a+b;
        case '-':
            return a-b;
        case '*':
            return a*b;
        case '/':
            return a/b;
        case '^':
            return pow(a, b);
    }
}
double solve()//求解后缀表达式eq 
{
    int lp = p--;
    if(eq[lp].c == '\0')//如果是数字 
        return eq[lp].n;
    else
    {
        double b = solve();
        double a = solve();
        return calc(a, b, eq[lp].c);
    }
}
int main()
{
    char s[25];
    double a, ans; 
	int n, ct = 0;//ct:输出个数 
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i)
	{
        scanf("%lf", &a);
        pq.push(a);
    }
    while(scanf("%s", s) != EOF)
    {
        if(s[0] >= '0' && s[0] <= '9' || s[0] == '-' && s[1] >= '0' && s[1] <= '9')//如果s是数字(可能是负数) 
        {
            eq[++p].n = atof(s);//将字符串s转为浮点数。
            eq[p].c = '\0'; 
        }
        else if(strcmp(s, "=") == 0)//遇到等号,求解后缀表达式 
        {
            ans = solve();
            printf("%e\n", ans);
            pq.pop(); 
            pq.push(ans);//将求解的结果存入堆 
            p = 0;
        }
        else//是+-*/^ 
        {
            eq[++p].n = 0;
            eq[p].c = s[0];
        }
    }
    putchar('\n');
    while(pq.empty() == false)
    { 
        printf("%e ", pq.top());
        ct++;
        if(ct == 10)
        {
            putchar('\n');
            ct = 0;
        }
        pq.pop();
    }
	return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值