【NOIP 2005】 等价表达式 题解

【NOIP 2005】 等价表达式 题解

昨天复习了一下表达式(栈)和图论相关的算法,结果卡在一道题上,上午做的等价表达式,下午才有所进展,最后好不容易写出来了个像样的程序,,但又因为落谷和codevs上数据有误(左右括号不匹配导致R RE,例如(a+2)^2)),,折腾半天,最后到vijos上也只是分多了点,,也没A掉。
两个栈是肯定的,一个用来存储数字,一个用来存储符号。
读入数字时,直接压入数字栈就可以了,没必要处理,
读入符号时:
1.如果是运算符,当前栈顶的运算符优先级大于等于新运算符,则将栈顶运算符弹出,并将当前数字栈顶的两个数进行相应运算,弹出旧数,压入新结果。不停循环,直到栈里面没有符号或符号优先级低于当前新运算符。
2.如果是“(”,直接压入栈。
3.如果是“)”,不需要压入栈,而是依次将栈里面的符号弹出,并计算。直到遇到一个”(“,然后将”(“也弹出(很关键,否则就会出现运算符数量大于等于数字的情况)。
那怎么知道符号栈的符号和哪两个数字对应呢,需不需要再开一个数组来记录?其实是不需要的,因为题目中不可能出现两个运算符连在一起的情况,即不会出现4+*3这种情况,即使是“(”或“)”和运算符连在一起,因为我们每遇到一个“)”就会弹出栈顶元素直至遇到“(”所以不必担心在弹出的过程中出现运算符大于等于数字的。
具体实现见如下代码

 /*
My convictions will not falter.--Poppy
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 32767
using namespace std;
const int test[]={0,2,3,5,7,11,13,41,121,421,4211};
long long sum1,sum2,sum3,n,i,ans1,ans2,ans3;
long long  num[1005];
char s[1005];
char ss[1005];
int high(char c)
{
    if(c=='+') return 1;
    if(c=='-') return 1;
    if(c=='*') return 2;
    if(c=='^') return 3;
    if(c=='(') return 0;
}
long long qmod(int x,int m,int k)
{
    long long a=1;
    x%=k;
    while(m)
    {
        if(m%2) a=(a*x)%k;
        x=x*x%k;
        m/=2;
    }
    return a%k;
}
long long work(int p1,int p2,int p3)
{
    if(s[p3]=='+')
    return (num[p1]+num[p2])%N;
    if(s[p3]=='-')
    return (num[p2]-num[p1]+N)%N;//这地方一定要写成(……+N)%N,因为别的运算都加了
    //这里如果不加的话,a取23,N取32767时,(a^4)%N-(3*(a^3)%N)%N是个负数,(a^4)%N=17705,而(3*(a^3)%N)%N是24334,相减会变成负数..
    //但实际上应该是a^4>3*a^3,用别的等价表达式可能算出来就是正数,然而这个点我以为是选取的N和a有关,盲目的提交了不下20遍.... 
    if(s[p3]=='*')
    return (num[p1]*num[p2])%N;
    if(s[p3]=='^')
    return qmod(num[p2],num[p1],N);
}
int getin(int su)
{
    int t=-1;
    int total,t1=0,t2=1;
    int p=1;
    s[1]='(';
    char c=' ';
    while(c!='\0'&&c!=-1)
    {
      c=ss[++t];
      if(c!=' ')
      { 
        if(c>=48&&c<=57)
        {
          if(p) t1++,num[t1]=0;
          num[t1]=(num[t1]*10+c-48)%N;
          p=0;
        }
        else if(c=='a')
          num[++t1]=su,p=1;
        else if(c=='(') 
          s[++t2]='(',p=1;
        else if(c==')'||c=='\0'||c==-1)
        {
          p=1;
          while(s[t2]!='('&&s[t2]!='\0'){
            num[t1-1]=work(t1,t1-1,t2);
            num[t1]=0;
            t1--;t2--;
          }
          t2--;
        }
        else 
        {
          while((high(c)<=high(s[t2]))) 
          {
          num[t1-1]=work(t1,t1-1,t2);
          num[t1]=0;
          t1--;t2--;
          }
          s[++t2]=c;
          p=1;
        }
      } 
    }
    return num[1]%N;
}
int read()
{
    char c=getchar();
    int x=0;
    while(c<48||c>57)
        c=getchar();
    while(c>=48&&c<=57)
        x=x*10+c-48,c=getchar();
    return x;
}
int main()
{
//  freopen("equal.in","r",stdin);
//  freopen("equal.out","w",stdout);
    gets(ss);
    sum2=getin(23);
    n=read();
    for(i=1;i<=n;i++)
    {
    memset(s,'\0',sizeof(s));
    gets(ss);
    ans2=getin(23);
    if(ans2==sum2) 
    printf("%c",i+64);
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值