信息学奥赛一本通 1962:【13NOIP普及组】表达式求值 | 洛谷 P1981 [NOIP2013 普及组] 表达式求值

【题目链接】

ybt 1962:【13NOIP普及组】表达式求值
洛谷 P1981 [NOIP2013 普及组] 表达式求值

【题目考点】

  1. 中缀表达式转后缀表达式,后缀表达式求值
  2. 中缀表达式求值

【解题思路】

表达式中只有加法和乘法,数字是 0 ∼ 2 31 − 1 0 \sim 2^{31}-1 02311,运算中不会出现负数。由于最后只需要最后4位,即结果%10000的值。根据同余定理:
( a + b ) % m = ( a % m + b % m ) % m (a+b)\%m = (a\%m+b\%m)\%m (a+b)%m=(a%m+b%m)%m ( a ∗ b ) % m = ( ( a % m ) ∗ ( b % m ) ) % m (a*b)\%m = ((a\%m)*(b\%m))\%m (ab)%m=((a%m)(b%m))%m
运算时,将每个数字都%10000,每次计算后的结果都%10000,最终得到的结果就是真实结果%10000后的值。
具体解法有两种

  1. 先将中缀表达式转为后缀表达式,而后求后缀表达式的值
  2. 直接求中缀表达式的值

【题解代码】

解法1:先将中缀表达式转为后缀表达式,而后求后缀表达式的值

中缀表达式转后缀表达式方法:

  1. 从左向右扫描表达式字符串。
  2. 遇到数字时,直接输出。
  3. 遇到运算符时,如果栈空,或该运算符优先级大于栈顶运算符优先级,则入栈。否则运算符出栈并输出。重复这一过程。
  4. 扫描结束后若栈中有运算符,都出栈并输出。

后缀表达式求值:

  1. 从左向右扫描字符串
  2. 遇到数字时,将数字入栈。
  3. 遇到运算符时,弹出栈顶的两个数,用该运算符对它们做相应的计算,结果入栈。
  4. 扫描结束后,栈顶数字就是结果。

本题解中,用一个数组e表示后缀表达式,数组中元素e[i]若大于等于0,表示数字。若e[i]为-1,表示乘号,若e[i]为-2,表示加号。这里乘号的值大于加号的值,可以表示乘号的优先级大于加号。

运算符最多 1 0 5 10^5 105个,那么运算符与数字加起来不会超过 3 ∗ 1 0 5 3*10^5 3105个,所以数组大小开为 3 ∗ 1 0 5 3*10^5 3105

#include 
using namespace std;
#define N 100000
#define M 10000
#define ADD -2 //加号
#define MULT -1//乘号
int e[3*N], e_i;//保存后缀表达式,e[i]若大于等于0,表示数字。若e[i]为-1,表示乘号,若e[i]为-2,表示加号。e_i:e数组待添加元素的位置
int stk[2*N], top;
int main()
{
    char c;
    int num = 0, cVal, a, b;//cVal:运算符的值:加号为-2,乘号为-1; a,b:运算数
    c = getchar();
    //输入并构建后缀表达式
    while(c != EOF && c != '\n')//不确定OJ输入的末尾是文件末尾EOF,还是换行符'\n',所以二者都可能是输入的结束符号,都判断一下。
    {
        if(c >= '0' && c <= '9')//c是数字
            num = (num * 10 + c - '0') % M;
        else//c是运算符
        {
            e[e_i++] = num;//后缀表达式添加num
            num = 0;
            if(c == '+')
                cVal = ADD;//如果是加号
            else
                cVal = MULT;//如果是乘号
            while(!(top == 0 || cVal > stk[top]))//如果栈中有运算符,且运算符c比栈顶运算符的优先级相等或更低,则运算符出栈,输出到后缀表达式
                e[e_i++] = stk[top--];
            stk[++top] = cVal;//此时运算符入栈
        }
        c = getchar();
    }
    e[e_i++] = num;//添加最后一个数字
    while(top > 0)//将栈中剩余的运算符加入后缀表达式
        e[e_i++] = stk[top--];
    //此时后缀表达式构建完成,开始根据后缀表达式求值
    for(int i = 0; i < e_i; ++i)
    {
        if(e[i] >= 0)//如果是数字
            stk[++top] = e[i];//数字入栈
        else//如果是运算符
        {
            a = stk[top--];//数字出栈
            b = stk[top--];//数字出栈
            if(e[i] == ADD)//如果是加号
                stk[++top] = (a + b) % M;//计算结果入栈
            else//如果是乘号
                stk[++top] = (a * b) % M;//计算结果入栈
        }
    }
    cout<<stk[top];//栈顶数值即为运算结果
    return 0;
}

解法2:直接求中缀表达式的值

中缀表达式求值:

  1. 设数字栈,运算符栈。从左向右扫描表达式
  2. 遇到数字进数字栈
  3. 遇到运算符,如果运算符栈栈空,或该运算符优先级大于栈顶运算符优先级,则入栈。否则运算符出栈,从数字栈出栈2个数字,进行计算,结果在数字栈入栈。重复这一过程。
  4. 扫描结束后,运算符栈出栈运算符,进行运算,直到运算符栈为空。最后数字栈栈顶的数字,就是结果。
#include
using namespace std;
#define N 100005
#define M 10000
int nStk[N], nTop, cTop;//nStk:数字栈 nTop:数字栈栈顶位置 cTop:运算符栈栈顶位置
char cStk[N], s[1000005];//cStk:运算符栈
int main()
{
    char tc;//c:读入的字符 tc:出栈的字符
    int num = 0, a, b, len;
    cin >> s;
    len = strlen(s);
    for(int i = 0; i <= len; ++i)
    {
        if(s[i] >= '0' && s[i] <= '9')//如果遇到数字字符
            num = (num * 10 + s[i] - '0') % M;
        else//如果遇到运算符
        {
            nStk[++nTop] = num;
            num = 0;
            while(!(cTop == 0 || s[i] == '*' && cStk[cTop] == '+'))//栈空或要入栈的运算符比栈顶运算符优先级高,才可以入栈,其他情况都出栈,做运算。
            {
                a = nStk[nTop--];//数字栈出栈两个数
                b = nStk[nTop--];
                tc = cStk[cTop--];//运算符栈出栈一个运算符,
                if(tc == '*')//进行计算,结果存入数字栈
                    nStk[++nTop] = (a * b) % M;
                else
                    nStk[++nTop] = (a + b) % M;
            }
            cStk[++cTop] = s[i];
        }
    }
    cout << nStk[nTop];//数字栈栈顶即为结果
    return 0;
}

同样解法,使用string读入,使用c++ stl的stack类来做

#include
using namespace std;
#define M 10000
stack<int> nStk;//nStk:数字栈 
stack<char> cStk;//cStk:运算符栈
string s;
int main()
{
    char c;
    int num = 0, a, b;
    cin >> s;
    for(int i = 0; i <= s.length(); ++i)
    {
    	if(s[i] >= '0' && s[i] <= '9')
			num = (num * 10 + s[i] - '0') % M;
		else
		{
			nStk.push(num);
			num = 0;
			while(!(cStk.empty() || s[i] ==	'*' && cStk.top() == '+'))
			{
				b = nStk.top(), nStk.pop();
				a = nStk.top(), nStk.pop();
				c = cStk.top(), cStk.pop();
				if(c == '*')
					nStk.push(a * b % M);
				else
					nStk.push((a + b) % M);
			}
			cStk.push(s[i]);
		}
	}
	cout << nStk.top();
    return 0;
}

你可能感兴趣的:(信息学奥赛一本通题解,NOIP真题解答,洛谷题解,NOIP,c++)