用并查集做表达式求值

每次在尚未运算的运算符找出一个优先级最高的符号,进行相应的运算,直至仅剩一个数字时返回。

这种方法符合自然思维和运算规则,实现简单明了,不易出错。

1.数据结构:
我们由左而右扫描表达式串st的每一个字符,存储每一个运算的信息。

其中第i个运算的运算符存储在c[i],优先级存储在d[i]

d[i]= (括号嵌套层数)*3+1 '+',-'
    (括号嵌套层数)*3+2 '*'
    (括号嵌套层数)*3+3 '^'

数存储在r[i](若第i个运算的操作数为变量a,则r[i]=-1)




我们将目前所有参与过运算的操作数放在集合里

其中第p个操作数参与运算后的结果记入第f[p]个操作数,即所有具有同一个f值的操作数组成一个集合,它们运算后的结果记入第f[p]个操作数。

如果c[i]为当前的运算符,则从f[i]和f[i+1]出发,顺着f指针即可找到参与运算的两个操作数。

const xn=3;{代入的a的总数}
    mn=2;{模的总数}
var
  cm,rc,nn,n,i,j:longint;{选项数为nn;运算数为n}
  st:string;{表达式串}
  c:array [1..30] of char;{c[i]为第i个运算的运算符}
  d:array [1..30] of longint;{ d[i]为第i个运算的优先级}
  r,u:array [1..30] of int64;{ r[i]为第i个运算的操作数。若r[i]=-1,则第i个运算的操作数为变量a}
  a:array [1..xn,1..mn] of int64;{将第i个变量值和第j个模代入题干后的值为a[i,j]}
  x:array [1..xn] of longint;{a的第i个代入值为x[i];模的第i个代入值为y[i]}
  y:array [1..mn] of longint;
  yes:boolean;{当前选项与题干等价的标志}
  mv:int64;{当前选项的值}


OI是一种精神

大家能再聚到一起不容易
所以珍惜每一次灌水的机会吧......
[楼 主] | Posted: 2006-08-20 11:47[顶端]
 
redswallow
大富翁勋章 优秀斑竹勋章 灌水天才勋章 论坛一周年纪念章
头衔:很好,很CCTV很好,很CCTV
该用户目前在线
级别: 鬼神
文章:7359 (20)
金钱:7024243
身价:10993
贡献值:22%
人品:32 Rp
Honey:潇湘南愁
家族门派: 脑抽乱水门
在线时间:2129(小时)
[UID:1] [传呼] [推荐] [引用] [编辑]



随 机 事 件 模仿蜡笔小新做卖火柴的小女孩,不慎烧伤手指头...扣去医药费22坛币


2.记录当前选项的每一个运算

procedure ff;
var top,i,j,v:longint;{i为读字符串的指针;top为括号嵌套重数,v为操作数}
begin
  top:=0;n:=1;i:=0;{括号嵌套重数、运算顺序数和字串指针初始化}
  fillchar(r,sizeof(r),0);{操作数序列初始化}
  while i<length(st) do
  begin
    inc(i);{字串指针右移}
    case st[i] of{分析第i个字符的类型}
    '(':inc(top);{若为左括号,括号嵌套重数+1}
    ')':dec(top); {若为右括号,括号嵌套重数-1}
'0'..'9':begin{若为数串,则计算对应的数字,记入r[n]。字串指针指向数串后的第一个字符}
      j:=i;v:=0;
      while(j<=length(st))and((st[j]>='0')and(st[j]<='9')or(st[j]=' '))do
      begin if st[j]<>' ' then v:=v*10+ord(st[j])-48;inc(j) end;{ while }
      r[n]:=v;i:=j-1
      end;{ '0'..'9'}
    'a': r[n]:=-1;
    ' ':continue;
else begin
    c[n]:=st[i];{记下运算符}
    case st[i] of{记下优先级}
    '+','-':d[n]:=top*3+1;
        '*':d[n]:=top*3+2;
        '^':d[n]:=top*3+3
    end;{ case }
inc(n){运算顺序+1}
end{ else }
end;{case}
end;{while}
end; {ff}


OI是一种精神

大家能再聚到一起不容易
所以珍惜每一次灌水的机会吧......
[1 楼] | Posted: 2006-08-20 11:51[顶端]
 
redswallow
大富翁勋章 优秀斑竹勋章 灌水天才勋章 论坛一周年纪念章
头衔:很好,很CCTV很好,很CCTV
该用户目前在线
级别: 鬼神
文章:7359 (20)
金钱:7024243
身价:10993
贡献值:22%
人品:32 Rp
Honey:潇湘南愁
家族门派: 脑抽乱水门
在线时间:2129(小时)
[UID:1] [传呼] [推荐] [引用] [编辑]



随 机 事 件 灌水大师亲临本贴,受此影响,本次发表1贴记6


3.求值过程

1.设b为运算完成的标志序列,其中b[i]=true,标志已完成c[i]运算符指定的运算。

2.在含有n个运算的表达式中,含有n-1个运算符。参与当前运算的两个操作数为r[p]和r[q]。我们采用类似并查集的方法计算表达式值,即初始时,N个操作数各组成n个集合。当计算r[p]←r[p] op r[q]后,集合q并入集合p (f[q]←p),表明第p个操作数和第q个操作数已经完成了运算,计算结果记入第p个操作数。

3.首先将n个变量操作数进行代值,并对n个操作数进行取摸运算。然后在未参与运算的运算符中寻找运算级最高的运算符c[max],d[max]=           。然后从max和max+1位置开始沿着f指针寻找两个待运算的操作数u[p]和u[q](f[p]=p,f[q]=q),完成由c[max]指定的运算,结果记入u[p],集合q并入集合p。b[max]←true。依次类推,直至完成n-1个运算为止。



function cal(x,m:longint):int64;{计算当a=x时,字符串st所代表的表达式结果模m的余数}
var
f:array [1..30] of longint;{单链表形式的并查集,f[i]=i表明第i个操作数为待运算的操作数}
  b:array [1..30] of boolean;{ 运算完成的标志序列}
  vv:int64;{乘幂的结果}
  i,j,max,p,q:longint;{当前参与运算的两个操作数为u[p]、u[q]}
begin
  u:=r;{记下操作数序列}
  for i:=1 to n do if u[i]=-1 then u[i]:=x;{变量代值}
  for i:=1 to n do u[i]:=((u[i] mod m)+m) mod m; {若u[i]是负数,则u[i] mod m也是负数,为避免这种情况,采用两次取模}
  for i:=1 to n do f[i]:=i;{ 并查集初始化}
  fillchar(b,sizeof(b),false);{ 初始时,所有运算未完成}
  for i:=1 to n-1 do
  begin
    max:=1;while b[max] do inc(max);{由左而右寻找第一个没有完成的运算序号max }
    for j:=max+1 to n-1 do{在max位置后寻找一个优先级最高的未完成运算}
    if (d[j]>d[max]) and not b[j] then max:=j;
    b[max]:=true;{完成该运算}
p:=max;while f[p]<>p do p:=f[p];{ 用类似并查集的方法寻找参与运算的两个操作数u[p]和u[q]}
    q:=max+1;while f[q]<>q do q:=f[q];
    f[q]:=p;{合并}
    case c[max] of{根据max位置的运算符进行运算}
    '+':u[p]:=(((u[p]+u[q]) mod m)+m) mod m;
    '-':u[p]:=(((u[p]-u[q]) mod m)+m) mod m;
    '*':u[p]:=(((u[p]*u[q]) mod m)+m) mod m;
    '^':begin
        vv:=1;
        for j:=1 to u[q] do vv:=((vv*u[p] mod m)+m) mod m;
        u[p]:=vv
        end{'^'}
    end;{ case }
  end;{ for }
  cal:=u[1]{返回代值后的结果}
end;{ cal }

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值