当前位置: 首页 > news >正文

UVa 198 Peter‘s Calculator

题目分析

Peter\texttt{Peter}Peter需要一个支持整数运算的计算器,功能包括:

  • 变量赋值,变量名由字母开头,后跟至多494949个字母或数字
  • 支持加法、减法、乘法运算
  • 表达式可以引用尚未定义的变量
  • PRINT\texttt{PRINT}PRINT指令输出变量的值,若变量未定义或存在循环依赖则输出UNDEF\texttt{UNDEF}UNDEF
  • RESET\texttt{RESET}RESET指令清空所有变量定义

变量是否未定义的判定条件:

  1. 变量从未被赋值
  2. 变量的定义中引用了未定义的变量
  3. 变量的定义中存在循环依赖

解题思路

本题的核心是处理带依赖关系的表达式求值。由于变量可以相互引用,且存在循环依赖的可能,需要在求值时检测循环。

整体框架

  1. 解析输入:每行可能是赋值、打印或重置指令
  2. 表达式解析:将中缀表达式转换为后缀表达式(逆波兰表示),方便后续计算
  3. 循环检测:在递归求值时,使用集合记录当前求值路径上的变量,若重复出现则检测到循环
  4. 延迟求值:变量被赋值时只存储其后缀表达式,实际求值在PRINT\texttt{PRINT}PRINT时进行

关键实现细节

1. 负数的处理

题目中的数字可以以负号开头(如−5-55)。解析时,将负号特殊标记(代码中用_代替),与减法运算符区分开。

判断条件:当-的前一个是运算符、括号或行首时,表示负数而非减法。

2. 中缀转后缀

使用调度场算法(Shunting-yard algorithm\texttt{Shunting-yard algorithm}Shunting-yard algorithm),定义优先级:

  • +-:优先级111
  • *:优先级222
  • ():优先级000
3. 后缀表达式求值与循环检测

递归求值时,维护一个全局集合undefined记录当前求值路径上的变量:

  • 进入求值某个变量时,将其加入集合
  • 若求值中再次遇到集合内的变量,说明存在循环依赖,返回失败
  • 求值完成后从集合中移除

代码实现

// Peter's Calculator// UVa ID: 198// Verdict: Accepted// Submission Date: 2016-03-17// UVa Run Time: 0.000s//// 版权所有(C)2016,邱秋。metaphysis # yeah dot net#include<bits/stdc++.h>usingnamespacestd;constintVARIABLE=0,OPERATOR=1,NUMBER=2;// 符号结构体:存储表达式中每个元素的信息structsymbol{string token;// 符号的字符串表示(变量名/运算符/数字字符串)intoperand;// 若为数字类型,存储其整数值intcategory;// 类型:VARIABLE(变量)、OPERATOR(运算符)、NUMBER(数字)};set<string>undefined;// 用于循环检测,记录当前求值路径上的变量map<string,vector<symbol>>formula;// 存储每个变量对应的后缀表达式map<string,int>priority;// 存储运算符的优先级// 递归计算后缀表达式的值// symbols: 后缀表达式序列// value: 输出参数,用于存储计算结果// 返回值: true表示计算成功,false表示变量未定义或存在循环依赖boolcalculate(vector<symbol>symbols,int&value){stack<symbol>operands;// 操作数栈for(inti=0;i<symbols.size();i++){symbol s=symbols[i];// 变量或数字直接入栈if(s.category==VARIABLE||s.category==NUMBER)operands.push(s);else{// 遇到运算符,弹出两个操作数symbol b=operands.top();operands.pop();symbol a=operands.top();operands.pop();intaa,bb,cc;// 处理左操作数 aif(a.category==VARIABLE){if(undefined.count(a.token)>0)returnfalse;if(formula.count(a.token)==0)returnfalse;undefined.insert(a.token);if(calculate(formula[a.token],aa)==false)returnfalse;undefined.erase(a.token);}elseaa=a.operand;// 处理右操作数 bif(b.category==VARIABLE){if(undefined.count(b.token)>0)returnfalse;if(formula.count(b.token)==0)returnfalse;undefined.insert(b.token);if(calculate(formula[b.token],bb)==false)returnfalse;undefined.insert(b.token);}elsebb=b.operand;// 执行运算if(s.token=="+")cc=aa+bb;elseif(s.token=="-")cc=aa-bb;elseif(s.token=="*")cc=aa*bb;// 将运算结果作为数字压入栈中operands.push((symbol){"",cc,NUMBER});}}// 栈顶即为最终结果if(operands.top().category==NUMBER){value=operands.top().operand;returntrue;}else{// 栈顶为变量,需要继续递归求值string variable=operands.top().token;if(undefined.count(variable)>0)returnfalse;if(formula.count(variable)==0)returnfalse;undefined.insert(variable);if(calculate(formula[variable],value))undefined.erase(variable);elsereturnfalse;}returntrue;}// 判断运算符 a 的优先级是否不高于运算符 bboollessPriority(string a,string b){returnpriority[a]<=priority[b];}// 中缀表达式转后缀表达式(逆波兰表达式)vector<symbol>infixToPostfix(vector<symbol>symbols){stack<symbol>operands,operators;// operands: 输出栈, operators: 运算符栈for(inti=0;i<symbols.size();i++){symbol s=symbols[i];// 数字或变量直接输出到 operands 栈if(s.category==NUMBER||s.category==VARIABLE)operands.push(s);else{// 左括号直接入运算符栈if(s.token=="(")operators.push(s);// 右括号:弹出运算符直到遇到左括号elseif(s.token==")"){while(!operators.empty()&&operators.top().token!="("){operands.push(operators.top());operators.pop();}// 弹出左括号if(!operators.empty())operators.pop();}else{// 当前运算符优先级高时直接入栈,否则弹出优先级较高的运算符if(operators.empty()||operators.top().token=="("||!lessPriority(s.token,operators.top().token))operators.push(s);else{while(!operators.empty()&&lessPriority(s.token,operators.top().token)){operands.push(operators.top());operators.pop();}operators.push(s);}}}}// 弹出剩余运算符while(!operators.empty()){operands.push(operators.top());operators.pop();}// 反转得到正确的后缀表达式顺序symbols.clear();while(!operands.empty()){symbols.insert(symbols.begin(),operands.top());operands.pop();}returnsymbols;}// 解析赋值语句voidparse(string line){// 删除所有空格for(inti=line.length()-1;i>=0;i--)if(isblank(line[i]))line.erase(line.begin()+i);// 分离左值和右值表达式string left=line.substr(0,line.find(":="));line=line.substr(line.find(":=")+2);// 将负数标记转换为特殊字符 '_',以便与减法运算符区分intindex=line.length()-1;while(index>=2){if(isdigit(line[index])&&line[index-1]=='-'){if(line[index-2]=='+'||line[index-2]=='-'||line[index-2]=='('||line[index-2]=='*'){line[index-1]='_';index-=2;}}index--;}// 处理行首的负号if(line.front()=='-')line[0]='_';// 词法分析:将字符串分割为 tokenindex=0;vector<symbol>symbols;while(index<line.length()){// 处理数字(包括负数)if((line[index]>='0'&&line[index]<='9')||line[index]=='_'){intsign=line[index]=='_'?-1:1;intoperands=0;if(line[index]=='_')index++;while(index<line.length()&&(line[index]>='0'&&line[index]<='9')){operands=operands*10+line[index]-'0';index++;}symbols.push_back((symbol){"",operands*sign,NUMBER});}// 处理变量名elseif(isalpha(line[index])){string block;while(index<line.length()){if(isalpha(line[index])||isdigit(line[index])){block+=line[index];index++;}elsebreak;}symbols.push_back((symbol){block,0,VARIABLE});}// 处理运算符else{symbols.push_back((symbol){string(1,line[index]),0,OPERATOR});index++;}}// 将中缀表达式转换为后缀表达式并存储formula[left]=infixToPostfix(symbols);}intmain(){// 初始化运算符优先级priority.insert(pair<string,int>("+",1));priority.insert(pair<string,int>("-",1));priority.insert(pair<string,int>("*",2));priority.insert(pair<string,int>("(",0));priority.insert(pair<string,int>(")",0));string line;while(getline(cin,line)){// 赋值语句if(line.find(":=")!=line.npos)parse(line);else{// PRINT 语句if(line.find("PRINT")!=line.npos){// 从行尾提取要打印的变量名intindex=line.length()-1;while(isblank(line[index]))index--;string block;while(!isblank(line[index])){block+=line[index];index--;}reverse(block.begin(),block.end());// 计算并输出结果undefined.clear();if(formula.count(block)>0){undefined.insert(block);intvalue;if(calculate(formula[block],value))cout<<value<<endl;elsecout<<"UNDEF"<<endl;}elsecout<<"UNDEF"<<endl;}// RESET 语句:清空所有变量定义elseformula.clear();}}return0;}

总结

本题的核心难点在于:

  1. 循环依赖检测:通过在递归求值路径上标记变量,遇到重复即判定为循环
  2. 负数解析:需要区分负数符号与减法运算符
  3. 表达式求值:采用中缀转后缀 + 栈求值的经典方法

上述代码通过递归求值和循环检测,能够正确处理变量间的复杂依赖关系,并在遇到未定义变量或循环时输出UNDEF\texttt{UNDEF}UNDEF

http://www.jsqmd.com/news/788727/

相关文章:

  • 别再乱用 /deep/ 了!聊聊 Vue scoped 样式隔离的利与弊,以及我的样式管理策略
  • 娱乐圈天降紫微星历史印证,海棠山铁哥延续李世民崛起轨迹
  • 如何快速无损剪辑视频音频:LosslessCut终极指南
  • OpenClaw智能体:开源GUI自动化与AI决策的融合实践
  • 基于图神经网络与强化学习的优化算法智能推荐系统
  • QQ音乐QMC格式转换终极指南:3步将加密音乐转为通用格式
  • 五分钟完成Nodejs后端对接Taotoken,为Web应用添加AI对话能力
  • 告别“乱码”与“不显示”:STM32 LCD1602驱动调试全记录,从时序分析到代码逐行调试
  • 专业代用茶礼盒厂家靠谱吗 - mypinpai
  • 记忆增强神经网络:如何让AI像人一样‘看一眼就记住’?
  • WorkshopDL:跨平台游戏玩家的终极Steam创意工坊下载解决方案
  • 基于Next.js与React构建AI对话界面前端模板的技术解析
  • 连续加班100天后,我身体垮了,但项目还是延期了
  • WELearn网课助手:让大学网课学习效率提升300%的智能神器
  • catlass ASWT策略说明
  • UVa 199 Partial Differential Equations
  • Sunshine自托管串流服务器:5大核心功能与跨平台部署指南
  • 2026年巴拿马移民定制公司推荐 - mypinpai
  • 利用cursor-profiles实现多开发环境隔离:原理、配置与实战
  • 实战指南:基于ArcGIS水文分析模块精准估算水库防洪库容
  • Sunshine游戏串流服务器:构建跨平台游戏体验的技术深度解析
  • 为什么越厉害的程序员,越不喜欢写注释?
  • 手把手教你用C语言写一个简易文件监控工具(基于Linux fanotify API)
  • 斐济移民价格贵吗? - mypinpai
  • 2026 天津婚纱摄影综合实力排名 |多维数据专业测评➕消费者决策指南 - charlieruizvin
  • 产品经理技能图谱:从T型到π型,构建结构化能力模型与实战指南
  • ArcMap数据驱动页面批量出图实战:从配置到PDF导出一站式指南
  • 从‘飞机大战’项目倒推:为了写游戏,我如何在Win10上搞定Python环境与pygame库?
  • 3分钟快速上手:Blender 3MF插件的完整使用指南
  • 避坑指南:OpenCV读取手机RTSP视频流卡顿、花屏?试试这3个优化参数