表达式去括号c语言,【BZOJ1561】[JSOI2009]去括号【表达式相关】

【题目链接】

一开始按照判断括号是否可以去掉的方法写,对着数据调了无数个小时,也就只过了一个点。

后来问Claris(%%%%)要了代码,发现原来还可以这么搞。

先用栈处理出每个左或右括号对应的另一个右或左括号的下标。

然后求每个运算符的优先级。

具体是这样的:

(1)定义一个临时变量j,表示当前位置的优先级,然后遍历表达式,初始j为1。

(2)进入一个括号j加2。(即遇到左括号)

(3)+和-的优先级直接赋j。

(4)*和/的优先级赋j+1。

(5)退出一个括号j减2。(即遇到右括号)

有一点要注意就是遇到 ((sth)) 这种情况,这种情况下优先级只加一次2,而不是加4。这种情况的判断是用预处理出来的括号位置搞的,不懂看代码就懂了。

预处理所有的优先级之后,就可以去括号了。

从1开始dfs。(1这个位置本来是不一定有括号的,我们要人为的给表达式两边再加一对括号,这样1这个位置就有括号了,方便处理)

每次dfs,先遍历串,处理之后的括号,保证正确性,同时还可以得到右括号的位置。

然后获取这两个括号左右位置的运算符的优先级,取最大的。

看括号内部是否有+或-号,这个不是直接看字符串,而是用优先级的大小关系判断。

如果有,那么不能去掉当前括号,返回。

如果没有,那么可以去掉,然后变号。

变号时,如果前面是减号,那么+变-,-变+。

如果前面是除号,那么*变/,/变*。

看代码吧。

%一发Claris

/* Pigonometry */

#include

#include

#include

using namespace std;

const int maxn = 305;

int n, pri[maxn], lbra[maxn], rket[maxn];

char str[maxn];

int sta[maxn];

inline int dfs(int l) {

int r;

for(r = l + 1; str[r] != ')'; r++)

if(str[r] == '(') r = dfs(r);

int k = max(pri[l - 1], pri[r + 1]), pos;

for(pos = l + 1; pos < r; pos++) if(pri[pos] == k + 1) break;

if(pos < r) return r;

str[l] = str[r] = '$';

int cnt = 0;

if(str[l - 1] == '-')

for(int i = l + 1; i < r; i++) {

if(str[i] == '(') cnt++;

else if(str[i] == ')') cnt--;

if(!cnt)

if(str[i] == '-') str[i] = '+';

else if(str[i] == '+') str[i] = '-';

}

if(str[l - 1] == '/')

for(int i = l + 1; i < r; i++) {

if(str[i] == '(') cnt++;

else if(str[i] == ')') cnt--;

if(!cnt)

if(str[i] == '*') str[i] = '/';

else if(str[i] == '/') str[i] = '*';

}

return r;

}

inline void solve() {

memset(pri, 0, sizeof(pri));

scanf("%s", str + 2); n = strlen(str + 2);

str[1] = '('; str[n + 2] = ')';

int top = 0;

for(int i = 1; i <= n; i++)

if(str[i] == '(') sta[++top] = i;

else if(str[i] == ')') rket[sta[top]] = i, lbra[i] = sta[top--];

pri[0] = pri[n + 1] = 1;

for(int i = 1, j = 1; i <= n; i++)

if(str[i] == '(' && (str[i - 1] != '(' || str[rket[i] + 1] != ')')) j += 2;

else if(str[i] == ')' && (str[i + 1] != ')' || str[lbra[i] - 1] != '(')) j -= 2;

else if(str[i] == '+' || str[i] == '-') pri[i] = j;

else if(str[i] == '*' || str[i] == '/') pri[i] = j + 1;

dfs(1);

for(int i = 2; i <= n + 1; i++)

if(str[i] != '$') printf("%c", str[i]);

printf("\n");

}

int main() {

int T; scanf("%d", &T);

while(T--) solve();

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
说明 很久就想编一个这样的计算器,只可惜一直没什么思路,最近突然灵感来了,所以就写下 这个程序。现在还在测试阶段,所以功能不是很完善。 程序功能:基本的表达式运算,可以自定义函数跟常量,分别保存在 “常数.txt” 和 “函数.txt”,方便自己添加。双击相应的函数名或常数名就可以将函数或常量添加到表达式中。 计算过程只能当表达式只有一行时有效。 实例1:计算sqr(19+tan(98)*tan(91)-sin(122)*(5*5-(19-11)))/2 计算过程sqr(19+tan(98)*tan(91)-sin(122)*(5*5-(19-11)))/2 =sqr(19+-7.11536972238419*tan(91)-sin(122)*(5*5-(19-11)))/2 =sqr(19+-7.11536972238419*-57.2899616307588-sin(122)*(5*5-(19-11)))/2 =sqr(19+-7.11536972238419*-57.2899616307588-.848048096156426*(5*5-(19-11)))/2 =sqr(19+-7.11536972238419*-57.2899616307588-.848048096156426*(5*5-8))/2 =sqr(19+-7.11536972238419*-57.2899616307588-.848048096156426*17)/2 =20.3032618253667/2 =10.1516309126834 实例2:计算 a=34 b=55 c=a+1 圆的面积(c) a*b c=a+b 圆的面积(c) 以下是计算结果: 圆的面积(c)=3848.4510006475 a*b=1870 圆的面积(c)=24884.5554090847 内置函数: !(x) - x 的阶乘 lg(x),log(x) 以10为底的对数 ln(x) 以 e为底x的对数 pow(x,y) x的y方次幂 prime(x) 判定x是否是素数,如果是直接将s2返回,否则将其各因子用连乘返回 sqr(x),sqrt(x) - x 的二次方根 arcsin(x) - x 的反正弦 arccos(x) - x 的反余弦 arcsec(x) - x 的反正割 arccsc(x) - x 的反余割 atn(x),arctg(x) - x 的反正切 arcctg(x) - x 的反余切 sin(x) - x 的正弦 cos(x) - x 的余弦 sec(x) - x 的正割 csc(x) - x 的余割 tg(x),tan(x) - x 的正切 ctg(x) - x 的余切 harcsin(x) - x 的反双曲正弦 harccos(x) - x 的反双曲余弦 harcsec(x) - x 的反双曲正割 harccsc(x) - x 的反双曲余割 harctg(x),harctan(x) - x 的反双曲正切 harcctg(x) - x 的反双曲余切 hsin(x) - x 的双曲正弦 hcos(x) - x 的双曲余弦 hsec(x) - x 的双曲正割 hcsc(x) - x 的双曲余割 htg(x),htan(x) - x 的双曲正切 hctg(x) - x 的双曲余切 有什么意见或建议可以跟我联系Email: ldm.menglv@gmail.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值