NOIP2013年普及组第二题:表达式求值

原文在这里:https://www.luogu.org/problemnew/show/P1981
好吧又是洛谷的。。。

首先,这道题很简单,由于只有乘法和加法两种运算,竟然还没有括号!!!简直就是普及组的奇迹!!!直接模拟不就好了?
直接上代码:

#include
#include
#include
#include
#include
#include
using namespace std;
const int mod=10000;
string s;
long long now=0,result=0,time=1;
bool isTime=false;

int main()
{
    freopen("expr.in","r",stdin);
    freopen("expr.out","w",stdout);
    cin>>s;
    int len=s.length();
    for(int i=0;iswitch(s[i]){
            case '+':{
                if(isTime==true){
                    result+=time*now;
                    result=result%mod;
                    isTime=false;
                    time=1;
                }
                else{
                    result+=now;
                    result=result%mod;
                }
                now=0;
                break;
            }
            case '*':{
                time*=now;
                time%=mod;
                isTime=true;
                now=0;
                break;
            }
            default:{
                if((s[i]>='0')&&(s[i]<='9'))
                    now=(now*10+s[i]-'0')%mod;
                break;
            }
        }
    }
    if(isTime){
        result+=time*now;
        result=result%10000;
    }
    else{
        result+=now;
        result=result%10000;
    }
    cout<return 0;
}

好了,如果单纯只是想了解这道题的做法的童鞋在这里就可以止步了。接下来,我们要推广到有加减乘除四则运算以及有括号参与的计算了。
在这里介绍两种方法:

1.用栈来实现:
首先用一个栈来存储数字,用一个栈来存储符号。如果进来的是左括号,那么先放着不做处理,如果进来的是加号和减号,就直接做到小括号为止。如果是乘号和除号,就做到第一个级别比他小的地方或者左括号为止。如果进来的是右括号,那么做到第一个左括号为止,并且删去该左括号,直到做到结束。这样在存数字的栈中唯一剩余的那一个数就是答案。
如果还是有点懵逼的童鞋可以看程序理解一下:

#include
#include
#include
#include
#include
#include
using namespace std;
const int maxN=100100;
const int mod=10000;
string s;
long long op1[maxN];
char op2[maxN];
int num1,num2;

void handle()
{
    switch(op2[num2]){
        case '+':
            op1[num1-1]=(op1[num1-1]+op1[num1])%mod;
            num1--; 
            break;
        case '-':
            op1[num1-1]=(op1[num1-1]-op1[num1])%mod;
            num1--;
            break;
        case '*':
            op1[num1-1]=(op1[num1-1]*op1[num1])%mod;
            num1--;
            break;
        case '/':
            op1[num1-1]=(op1[num1-1]/op1[num1])%mod;
            num1--;
            break;          
    }
}
void work1()
{
    while(num2>0&&op2[num2]!='('){
        handle();
        num2--;
    }
}

void work2()
{
    while(num2>0&&(op2[num2]=='*'||op2[num2]=='/')){
        handle();
        num2--;
    } 
} 

int main()
{
    //freopen("expr.in","r",stdin);
    //freopen("expr.out","w",stdout);
    cin>>s;
    int len=s.length();
    int i=0;
    while(iif(s[i]<='9'&&s[i]>'0'){
            long long temp=0;
            while(i='0'&&s[i]<='9'){
                temp=(temp*10+s[i]-'0')%mod;
                i++;
            }
            num1++;
            op1[num1]=temp;
        }
        if(i==len)
            break;
        switch(s[i]){
            case '+':
            case '-':
                    work1();
                    op2[++num2]=s[i];
                    break;
            case '*':
            case '/':
                    work2();
                    op2[++num2]=s[i];
                    break;
            case '(':
                    op2[++num2]=s[i];
                    break;
            case ')':
                    work1();
                    num2--; 
                    break;
        }
        ++i;
        cout<for(int i=1;i<=num1;++i)
            cout<" ";
        cout<cout<for(int i=1;i<=num2;++i)
            cout<" ";
        cout<cout<1]<return 0;
}

2.递归
说实话,用栈来实现比用递归来实现要快多了,但是为了锻炼自己,还是要每一种方法都要写一下。
先讲一下思路吧:首先我们要找到最后一次运算的符号在哪里,并且从这分开,在他的左右两边去寻找各自区间的最后运算的符号在哪里,然后以此类推,直到这个区间所有的都是数字为止。
首先,我们要解决括号配对的问题。我们依然可以用栈的思想来实现,并且我们还要记录每一个点他外面所拥有的括号层数,以此来判断是否为最后一次运算的符号。说完思路,可能大家还是有点蒙蒙的,举一个栗子好了:
假如说我们要计算 (1+2)*3 的值
用栈将 s[0] 的左括号和 s[4] 的右括号匹配,s[1]到s[3]的外层括号数都为1
首先递归0到(len-1)寻找最后做的运算符号,发现是s[5]的乘号,所以再递归两边的最后的符号,即递归0到4,和递归6到6,将二者的值乘起来就是答案。

下面考虑6到6:由于是一个数字,所以结束递推,返回值s[6]
再来考虑0到4:由于s[0]和s[4]是一对匹配的左右括号,所以直接考虑1到3,1+2…这个就不必再往下了吧?
最后附上代码~~~

#include
#include
#include
#include
#include
#include
using namespace std;
const int maxN=100100;
string s;
int len,ans,stack[maxN],stacknum=0,kuo[maxN],kuonum=0;
int peidui[maxN];

bool isDigit(int l,int r)
{
    for(int i=l;i<=r;++i){
        if(s[i]<'0'||s[i]>'9')
            return false;
    }
    return true;
}

int dg(int l,int r)
{
    if(isDigit(l,r)){
        int t=s[l]-'0';
        for(int i=l+1;i<=r;++i)
            t=t*10+s[i]-'0';
        return t;
    }
    if(peidui[l]==r){
        for(int i=l+1;i<=r-1;++i)
            kuo[i]--;
        return dg(l+1,r-1);
    }
    char p='.';
    int p1=-1;
    for(int i=l+1;i<=r-1;++i){
        switch(s[i]){
            case '+':
            case '-':
                if(kuo[i]==0){
                    p=s[i];
                    p1=i;
                }
                break;
            case '*':
            case '/':
                if(kuo[i]==0){
                    if(p1==-1){
                        p1=i;
                        p=s[i];
                    }
                    else{
                        if(p=='*'||p=='/'){
                            p1=i;
                            p=s[i];
                        }
                    }
                }
                break;
        }
    }
    switch(p){
        case '+':
            return dg(l,p1-1)+dg(p1+1,r);
        case '-':
            return dg(l,p1-1)-dg(p1+1,r);
        case '/':
            return dg(l,p1-1)/dg(p1+1,r);
        case '*':
            return dg(l,p1-1)*dg(p1+1,r);
    }
}

int main()
{
    cin>>s;
    len=s.size();
    for(int i=0;iif(s[i]=='('){
            stack[++stacknum]=i;
            kuonum++;
            kuo[i]=-1;
            continue;
        }
        if(s[i]==')'){
            peidui[stack[stacknum]]=i;
            peidui[i]=stack[stacknum];
            stacknum--;
            kuonum--;
            continue;
        }
        kuo[i]=kuonum;
    }
    cout<0,len-1)<return 0;
}

你可能感兴趣的:(C++,Junior,NOIP)