2016 河南省第九届ACM程序设计大赛 nyoj 1272 表达式求值

6 篇文章 0 订阅
1 篇文章 0 订阅

                                                                                   表达式求值
时间限制:1000 ms | 内存限制:65535 KB
难度:3

描述
假设表达式定义为: 1. 一个十进制的正整数 X 是一个表达式。 2. 如果 X 和 Y 是 表达式,则 X+Y, X*Y 也是表达式; *优先级高于+. 3. 如果 X 和 Y 是 表达式,则 函数 Smax(X,Y)也是表达式,其值为:先分别求出 X ,Y 值的各位数字之和,再从中选最大数。 4.如果 X 是 表达式,则 (X)也是表达式。 例如: 表达式 12*(2+3)+Smax(333,220+280) 的值为 69。 请你编程,对给定的表达式,输出其值。

输入
【标准输入】 第一行: T 表示要计算的表达式个数 (1≤ T ≤ 10) 接下来有 T 行, 每行是一个字符串,表示待求的表达式,长度<=1000
输出
【标准输出】 对于每个表达式,输出一行,表示对应表达式的值。
样例输入

3
12+2*3
12*(2+3)
12*(2+3)+Smax(333,220+280)

样例输出

18
60
69

来源
河南省第九届省赛


去年我们队对于这道题有点懵比,因为我们用数组模拟的话,那情况就太特多了,五个小时也不一定可以把所有的情况写完,而且这样的话,代码肯定很长很长,估计出问题了,也不好找哪里错了,所有就没有过。

今年再次看到这道题,队友提醒说可以用数据结构的表达式求值来算,也就是把中缀表达式转换成前缀表达式或者后缀表达式(哎,只怪自己对数据结构学的还不够好啊)。

如果不清楚中缀怎麼转换后缀的,请先看这篇文章文章

这道题我是转换成后缀来算的,那么首先我们要知道后缀的结构,也就是操作数在前,操作运算符在后。

例如:(a+b)*c+d,把这个式子转成后缀为a#b#+#*#c#+#d#,之后我们再对后缀表达式求值即可。

但是这道题我们需要对 ' , ' 和' S '定义运算符的大小,这样有嵌套的时候不至于乱套。

那题目上的数据来说吧。

12*(2+3)+Smax(333,220+280)

按照规则中缀转后缀的规则我们可以知道转换之后的表达式为12#2#3#+#*#333#220#280#+#,#S#+#,再按照我们的计算规则就可以算出结果了,但是要是这一组数据呢,12*(2+3)+Smax(10+Smax(100,200),Smax(220+280,1000)),正确的后缀表达式应该是

12#2#3#+#*#10#100#200#,#S#+#220#280#+#1000#,#S#,#S#+#,为什么呢?因为如果里面有嵌套的话,那么我们一定要先把嵌套在里面的表达式求出来,这样再求外面的值。

Smax(10+Smax(100,200),Smax(220+280,1000)),如果我们没有定义‘ , ’和‘ S ’的运算符级别,那么后缀就会成为10#100#200#,#220#280#1000#,#+#S#,#S#+#S#,如果这样算的话,结果事不对的。

再和上面正确的后缀表达式比较,就可以再找出来不同了。

接下来,我们再对后缀表达式进行计算,从左往右找的时候,如果是操作数,那么就入栈,直到有运算符的时候,我们取栈的前两个元素,对他们进行运算,就这样一直找到最后即可,就能得到我们想要的答案了。


AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <cstring>
using namespace std;
char a[1005];
string S1,S2;

int jibie(char a)
{
    int flag;
    switch(a)
    {
        case 'S': flag = 4;break;
        case ',': flag = 1;break;
        case '+': flag=2;break;
        case '-':flag=2;break;
        case '*':flag=3;break;
        case '/':flag=3;break;
        default :flag=0;
    }
    return flag;
}
void conversion()  ///把中缀表达式转换成后缀表达式
{
    stack<char>Q;
    char a;
    a='#';
    Q.push(a);
    int i=0,m=S1.length();
    while(i<m)
    {
        a=Q.top();
        if(S1[i]=='('||S1[i]=='S')
          {
              if(S1[i]=='(')
              {
                  Q.push(S1[i]);
                  i++;
              }
              else
              {
                 Q.push(S1[i]);
                 i+=4;
                 //printf("^^^^^^%c\n",S1[i]);
              }

          }
        else if(S1[i]==')')
        {
            while(a!='(')
            {
                S2=S2+a+'#';
                Q.pop();
                a=Q.top();
            }
            Q.pop();
            i++;
        }
        else if(S1[i]=='+'||S1[i]=='-'||S1[i]=='*'||S1[i]=='/'||S1[i]==',')
        {
            while(jibie(a)>=jibie(S1[i]))
            {
                S2=S2+a+'#';
                Q.pop();
                a=Q.top();
            }
            Q.push(S1[i]);
            i++;
        }
        else
        {
            //printf("************%c\n",S1[i]);
            while((S1[i]>='0'&&S1[i]<='9')||S1[i]=='.')
            {
                S2=S2+S1[i];
                i++;
            }
            S2+='#';
        }
    }
    //cout << S1 << endl;
    while(!Q.empty())
    {
        a=Q.top();
        Q.pop();
        if(a!='#')
        {
            S2=S2+a+'#';
        }
    }
    cout<<S2<<endl;
}
void Zhi()  ///对后缀表达式求值
{
    int m=S2.length();
    int i=0;
    int num=0,a,b;
    stack<int>W;
    int num1=1;
    while(i<m){
         switch(S2[i])
         {
             case '+': a=W.top();W.pop();b=W.top();W.pop();a=a+b;W.push(a);i++;break;
             case '-': a=W.top();W.pop();b=W.top();W.pop();a=b-a;W.push(a);i++;break;
             case '*': a=W.top();W.pop();b=W.top();W.pop();a=a*b;W.push(a);i++;break;
             case '/': a=W.top();W.pop();b=W.top();W.pop();a=b/a;W.push(a);i++;break;
             case 'S':
                 {
                     a = W.top(); W.pop(); b = W.top(); W.pop();
                     int sum1 = 0, sum2 = 0;
                     //printf("a = %d, b = %d\n",a, b);
                     while(a!=0)
                     {
                         sum1 += a % 10;
                         a/=10;
                     }
                     while(b!=0)
                     {
                         sum2 += b % 10;
                         b/=10;
                     }
                     //printf("sum1 = %d sum2 = %d\n",sum1, sum2);
                     int maxn = max(sum1, sum2);
                     W.push(maxn);
                     i++;
                     break;
                 }
             case ',': i++;break;
             default:
                {
                    if(S2[i]>='0'&&S2[i]<='9'&&num1==1){
                        num=num*10+S2[i]-'0';
                        if((S2[i+1]<'0'&&S2[i+1]!='.')||(S2[i+1]>'9'&&S2[i+1]!='.'))
                        {
                            //printf("bbbbbbbbbbb%lf\n",num);
                            W.push(num);
                        }
                    }
                    else if(S2[i]=='.') {
                        num1=num1*10;
                    }
                    else if(S2[i]>='0'&&S2[i]<='9'&&num1!=1) ///如果数据有小数  在这里处理
                    {
                        num=num+(S2[i]-'0')/(num1*1.0);
                        num1=num1*10;
                         if(S2[i+1]<'0'||S2[i+1]>'9'){
                            W.push(num);
                             //printf("bbbbbbbbbbb%lf\n",num);
                        }
                    }
                    else
                    {
                        num1=1;
                        num=0.0;
                    }
                    i++;
                }
         }
    }
    printf("%d\n",W.top());
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        getchar();
        cin>>S1;
        S2="";
        conversion();
        Zhi();
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值