noip2005 等价表达式 (判断表达式是否相等,对 mod 取模)

P1003等价表达式

描述

明明进了中学之后,学到了代数表达式。有一天,他碰到一个很麻烦的选择题。这个题目的题干中首先给出了一个代数表达式,然后列出了若干选项,每个选项也是一个代数表达式,题目的要求是判断选项中哪些代数表达式是和题干中的表达式等价的。

这个题目手算很麻烦,因为明明对计算机编程很感兴趣,所以他想是不是可以用计算机来解决这个问题。假设你是明明,能完成这个任务吗?

这个选择题中的每个表达式都满足下面的性质:

1. 表达式只可能包含一个变量‘a’。

2. 表达式中出现的数都是正整数,而且都小于10000。

3. 表达式中可以包括四种运算‘+’(加),‘-’(减),‘*’(乘),‘^’(乘幂),以及小括号‘(’,‘)’。小括号的优先级最高,其次是‘^’,然后是‘*’,最后是‘+’和‘-’。‘+’和‘-’的优先级是相同的。相同优先级的运算从左到右进行。(注意:运算符‘+’,‘-’,‘*’,‘^’以及小括号‘(’,‘)’都是英文字符)

4. 幂指数只可能是1到10之间的正整数(包括1和10)。

5. 表达式内部,头部或者尾部都可能有一些多余的空格。

下面是一些合理的表达式的例子:

((a^1) ^ 2)^3,a*a+a-a,((a+a)),9999+(a-a)*a,1 + (a -1)^3,1^10^9……

对于30%的数据,表达式中只可能出现两种运算符‘+’和‘-’;

对于其它的数据,四种运算符‘+’,‘-’,‘*’,‘^’在表达式中都可能出现。

对于全部的数据,表达式中都可能出现小括号‘(’和‘)’。

格式

输入格式

输入的第一行给出的是题干中的表达式。第二行是一个整数n(2 <= n <= 26),表示选项的个数。后面n行,每行包括一个选项中的表达式。这n个选项的标号分别是A,B,C,D……

输入中的表达式的长度都不超过50个字符,而且保证选项中总有表达式和题干中的表达式是等价的。

输出格式

输出包括一行,这一行包括一系列选项的标号,表示哪些选项是和题干中的表达式等价的。选项的标号按照字母顺序排列,而且之间没有空格。

样例1

样例输入1[复制]

( a + 1) ^2
3
(a-1)^2+4*a
a  + 1+ a
a^2 + 2 * a * 1 + 1^2 + 10 -10 +a -a

样例输出1[复制]

AC

限制

1s

来源

NOIp2005 第四题

解析:若两个表达式是相同的,那么对于任意一个数 x ,他们的运算结果是相同的。那么,思路就确定了,选择 n 个不同的数(怎么选看自己,可以随机,也可以直接1 到 n,不过个人觉得随机兴许要好点吧),然后将其值带入每个表达式,计算表达式的值,只要有一个不相等,那么这两表达式就是不同的。

         好了,巨坑的地方来了,如果在表达式出现2^32这种数据,如果存储方式是 int 的话,直接就变成 0 了。对于这种情况,要如何处理呢?高精度?太麻烦了吧。

        我们可以确定一个数 mod ,在计算表达式的值时,在每一步计算时都对 mod 取余(我想这也是题目上并没有 "/" 的原因吧)。如果表达式是相等的,那么他们进过这样的每步取模运算之后的结果也是相同的。

        为了避免偶然性,我觉得最好是随机生成一个 mod 数列(我这里只用了一个数,就不再修改了)。  

代码:

#include<cstdio>
#include<cctype>
#include<cstring>
#include<cstdlib>
#include<ctime>
#define maxn 50
#define max_len 10
using namespace std;

int x[max_len],p[maxn+20],q[maxn+20];
char a[maxn+20],b[maxn+20];
int mod;

int power(int i,int j)
{
  int ans=1;
  while(j>0)
    {
      if(j&1)ans*=i,ans%=mod;
      i*=i,i%=mod,j>>=1;
    }
  return ans;  
}

void work()
{
  switch(p[p[0]])
    {
      case 1:q[q[0]-1]+=q[q[0]];
             q[q[0]-1]%=mod; 
             break;
	  case 2:q[q[0]-1]-=q[q[0]];
	         q[q[0]-1]%=mod;
	         break;
	  case 3:q[q[0]-1]*=q[q[0]];
	         q[q[0]-1]%=mod;
	         break;
	  case 4:q[q[0]-1]=power(q[q[0]-1],q[q[0]]);
	         break; 
	  case 5:p[0]--;
	         return; 
	  case 6:p[0]--;
	         return;       
    }
  p[0]--,q[0]--;  
}

int get(char s[],int x)
{
  int i,j,k,len=strlen(s);
  p[0]=0,q[0]=1,q[1]=0;//比如:- a 
  for(i=0;i<len;i++)
    {
      if(s[i]==' ')continue;
      if(s[i]=='a')
	    {
		  q[++q[0]]=x;
		  continue;
	    }
	  if(isdigit(s[i]))
	    {
	      q[++q[0]]=s[i]-'0';
	      while(isdigit(s[i+1]))
	        q[q[0]]=q[q[0]]*10+s[++i]-'0',q[q[0]]%=mod;
	      continue;  
	    }  
      switch(s[i])
        {
          case '(':p[++p[0]]=5;
		           break;
          case '+':while(p[0]>0 && p[p[0]]>0 && p[p[0]]<5)work();
                   p[++p[0]]=1;  
		           break;
          case '-':while(p[0]>0 && p[p[0]]>0 && p[p[0]]<5)work();
                   p[++p[0]]=2; 
		           break;
          case '*':while(p[0]>0 && p[p[0]]>2 && p[p[0]]<5)work();
                   p[++p[0]]=3;
		           break;
          case '^':while(p[0]>0 && p[p[0]]>3 && p[p[0]]<5)work();
                   p[++p[0]]=4;
		           break;
          case ')':while(p[0]>0 && p[p[0]]<5)work();
                   if(p[p[0]]==5)p[0]--;
				   break;  
        }
    }
  while(p[0])work();
  if(q[0]==1)return (q[1]+mod)%mod;
  return (q[2]+mod)%mod;  
}

int main()
{
  srand(time(0));
  mod=rand()%10000+100;
  
  int i,j,k,n;
  gets(a);
  for(i=1;i<max_len;i++)
    x[i]=get(a,i);
  scanf("%d\n",&n);
  for(i=0;i<n;i++)
    {
      gets(b);
      for(j=1;j<max_len;j++)
        if(get(b,j)!=x[j])break;
      if(j>=max_len)printf("%c",'A'+i);  
    }
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值