[NOIP2005 提高组] 等价表达式
题目描述
明明进了中学之后,学到了代数表达式。有一天,他碰到一个很麻烦的选择题。这个题目的题干中首先给出了一个代数表达式,然后列出了若干选项,每个选项也是一个代数表达式,题目的要求是判断选项中哪些代数表达式是和题干中的表达式等价的。
这个题目手算很麻烦,因为明明对计算机编程很感兴趣,所以他想是不是可以用计算机来解决这个问题。假设你是明明,能完成这个任务吗?
这个选择题中的每个表达式都满足下面的性质:
- 表达式只可能包含一个变量 a \tt a a。
- 表达式中出现的数都是正整数,而且都小于 10000 10000 10000。
- 表达式中可以包括四种运算
+
(加),-
(减),*
(乘),^
(乘幂),以及小括号()
。小括号的优先级最高,其次是^
,然后是*
,最后是+
和-
。+
和-
的优先级是相同的。相同优先级的运算(包括^
运算)都是从左到右进行。 - 幂指数只可能是 1 1 1 到 10 10 10 之间的正整数(包括 1 1 1 和 10 10 10)。
- 表达式内部,头部或者尾部都可能有一些多余的空格。
下面是一些合理的表达式的例子:
((a^1) ^ 2)^3
,a*a+a-a
,((a+a))
,9999+(a-a)*a
,1 + (a -1)^3
,1^10^9
输入格式
第一行给出的是题干中的表达式。
第二行是一个整数 n n n,表示选项的个数。后面 n n n行,每行包括一个选项中的表达式。这 n n n 个选项的标号分别是 $A,B,C,D\cdots $
输入中的表达式的长度都不超过 50 50 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
提示
- 对于
30
%
30\%
30% 的数据,表达式中只可能出现两种运算符
+
和-
; - 对于其它的数据,四种运算符
+-*^
在表达式中都可能出现。 - 对于
100
%
100\%
100% 的数据,表达式中都可能出现小括号
()
, 2 ≤ n ≤ 26 2 \le n \le 26 2≤n≤26。
【题目来源】
NOIP 2005 提高组第四题(洛谷)
题解
#include <cstdio>
#include <algorithm>
#include <cstdlib>
using namespace std;
const int mod = 1000000007; // 定义模数
const int a = 1231; // 定义变量 a 的替换值
int n, l[30], x, la;
char s[30][100], c, p[100];
// 计算次幂的函数
int Pow(int x, int y) {
long long a = 1;
for (int i = 0; i < y; i++)
a = a * x % mod;
return a;
}
// 解析表达式的辅助函数
int js1(int b, int e) {
int t = 0, a, bb, c;
a = bb = c = -1;
for (int i = b; i <= e; i++) {
if (p[i] < '0' || p[i] > '9') break;
t = t * 10 + p[i] - '0';
if (i == e) return t;
}
t = 0;
for (int i = b; i <= e; i++) {
if (p[i] == '(') t++;
if (p[i] == ')') t--;
if (!t) {
if (p[i] == '+' || p[i] == '-') a = i;
if (p[i] == '*') bb = i;
if (p[i] == '^') c = i;
}
}
if (a != -1) {
if (p[a] == '+')
return (((long long)js1(b, a - 1) + js1(a + 1, e)) % mod + mod) % mod;
else
return (((long long)js1(b, a - 1) - js1(a + 1, e)) % mod + mod) % mod;
}
if (bb != -1)
return (((long long)js1(b, bb - 1) * js1(bb + 1, e)) % mod + mod) % mod;
if (c != -1)
return (Pow(js1(b, c - 1), js1(c + 1, e)) % mod + mod) % mod;
return js1(b + 1, e - 1);
}
// 检查括号是否匹配的函数
bool kuohao(int x) {
int h = 0;
for (int i = 0; i < l[x]; i++) {
if (s[x][i] == '(') h++;
if (s[x][i] == ')') h--;
if (h < 0) return false;
}
return !h;
}
// 解析表达式的主函数
int js(int x, int b, int e) {
int t = 0, a, bb, c;
a = bb = c = -1;
for (int i = b; i <= e; i++) {
if (s[x][i] < '0' || s[x][i] > '9') break;
t = t * 10 + s[x][i] - '0';
if (i == e) return t;
}
t = 0;
for (int i = b; i <= e; i++) {
if (s[x][i] == '(') t++;
if (s[x][i] == ')') t--;
if (!t) {
if (s[x][i] == '+' || s[x][i] == '-') a = i;
if (s[x][i] == '*') bb = i;
if (s[x][i] == '^') c = i;
}
}
if (a != -1) {
if (s[x][a] == '+')
return (((long long)js(x, b, a - 1) + js(x, a + 1, e)) % mod + mod) % mod;
else
return (((long long)js(x, b, a - 1) - js(x, a + 1, e)) % mod + mod) % mod;
}
if (bb != -1)
return (((long long)js(x, b, bb - 1) * js(x, bb + 1, e)) % mod + mod) % mod;
if (c != -1)
return (Pow(js(x, b, c - 1), js(x, c + 1, e)) % mod + mod) % mod;
return js(x, b + 1, e - 1);
}
int main() {
// 读取题干表达式
c = getchar();
while (c != '\n' && c != '\r') {
if (c != ' ') {
if (c != 'a')
p[la++] = c;
else {
p[la++] = '1';
p[la++] = '2';
p[la++] = '3';
p[la++] = '1';
}
}
c = getchar();
}
// 读取选项数量
scanf("%d", &n);
// 读取选项表达式
for (; x < n; x++) {
c = getchar();
while (c == '\n' || c == '\r') c = getchar();
while (c != '\n' && c != '\r') {
if (c != ' ') {
if (c != 'a') {
s[x][l[x]++] = c;
} else {
s[x][l[x]++] = '1';
s[x][l[x]++] = '2';
s[x][l[x]++] = '3';
s[x][l[x]++] = '1';
}
}
c = getchar();
}
}
// 计算题干表达式的哈希值
int hhh = js1(0, la - 1);
// 比较选项表达式与题干表达式
for (int i = 0; i < n; i++) {
if (kuohao(i) && js(i, 0, l[i] - 1) == hhh)
printf("%c", 'A' + i);
}
return 0;
}