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

UVa 12803 Arithmetic Expressions

题目分析

本题要求对给定的算术表达式进行求值,并按照特定格式输出结果。这是一个典型的表达式求值问题,但需要特别注意输入格式、精度处理和输出格式的要求。

输入格式分析

  • 第一行包含一个自然数 NNN,表示需要求值的表达式数量
  • 接下来的 NNN 行,每行包含一个算术表达式
  • 表达式采用完全括号化的形式,确保运算优先级明确
  • 所有符号(括号、运算符、数字)之间用空格分隔
  • 数字固定为两位小数格式
  • 支持的运算符包括:+\texttt{+}+-\texttt{-}-*\texttt{*}*/\texttt{/}/

输出要求分析

  • 每个表达式输出一行,包含求值结果
  • 结果必须保留两位小数
  • 需要进行四舍五入处理
  • 特别注意负零情况的处理:−0.00499999-0.004999990.00499999 要输出为 −0.00-0.000.00
  • 结果绝对值不超过 200000020000002000000
  • 保证不会出现除零错误

解题思路

1 1 1. 词法分析( Tokenization \texttt{Tokenization} Tokenization

由于输入中所有符号都用空格分隔,理论上可以直接按空格分割。但实际测试发现部分输入可能存在空格缺失的情况,特别是负号与数字之间。因此需要实现一个健壮的词法分析器,能够正确识别:

  • 数字(包括负数)
  • 运算符
  • 括号

关键难点在于区分一元负号(表示负数)和二元减号(表示减法运算)。判断规则为:

  • 如果负号出现在表达式开头、左括号后或其他运算符后,则为负号
  • 否则为减号运算符
2 2 2. 语法分析( Parsing \texttt{Parsing} Parsing

表达式语法采用递归定义:

Expression → Real | '(' Expression Op Expression ')'
Op → '+' | '-' | '*' | '/'
Real → [-]d.dd(两位小数)

由于输入是完全括号化的,可以使用递归下降分析法,算法流程为:

  • 如果当前 token\texttt{token}token(\texttt{(}(,则递归解析左表达式、运算符、右表达式
  • 否则,当前 token\texttt{token}token 是数字,直接返回其数值
3 3 3. 精度处理

题目要求不能使用浮点数表示,但可以在计算过程中使用浮点数,最终输出时进行格式化和四舍五入。四舍五入规则为:

  • 0.0050.0050.005 舍入为 0.010.010.01
  • 0.004999990.004999990.00499999 舍入为 0.000.000.00
  • −0.005-0.0050.005 舍入为 −0.01-0.010.01
  • −0.00499999-0.004999990.00499999 舍入为 −0.00-0.000.00
4 4 4. 算法复杂度
  • 时间复杂度:O(L)O(L)O(L),其中 LLL 是表达式长度
  • 空间复杂度:O(L)O(L)O(L),用于存储 tokens\texttt{tokens}tokens 和递归调用栈

代码实现

// Arithmetic Expressions
// UVa ID: 12803
// Verdict: Accepted
// Submission Date: 2025-10-
// UVa Run Time: 0.000s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
// 词法分析函数:将输入字符串分解为token序列
vector<string> tokenize(const string& line) {vector<string> tokens;string current;for (size_t i = 0; i < line.length(); i++) {char c = line[i];if (isspace(c)) {if (!current.empty()) {tokens.push_back(current);current.clear();}} else if (c == '(' || c == ')' || c == '+' || c == '*' || c == '/') {if (!current.empty()) {tokens.push_back(current);current.clear();}tokens.push_back(string(1, c));} else if (c == '-') {// 区分负号和减号:负号出现在表达式开头、左括号后或运算符后if (current.empty() && (tokens.empty() || tokens.back() == "(" || tokens.back() == "+" || tokens.back() == "-" || tokens.back() == "*" || tokens.back() == "/")) {current += c;  // 负号,作为数字的一部分} else {if (!current.empty()) {tokens.push_back(current);current.clear();}tokens.push_back(string(1, c));  // 减号运算符}} else {current += c;  // 数字部分}}if (!current.empty()) {tokens.push_back(current);}return tokens;
}
// 字符串转浮点数
double str_to_double(const string& s) {double value;sscanf(s.c_str(), "%lf", &value);return value;
}
// 浮点数转字符串,处理四舍五入和负零情况
string double_to_str(double value) {double rounded = round(value * 100) / 100;  // 四舍五入到两位小数// 处理负零情况:原始值为负且舍入后接近零if (fabs(rounded) < 1e-10 && value < 0) {return "-0.00";}if (fabs(rounded) < 1e-10) {return "0.00";}char buffer[20];snprintf(buffer, sizeof(buffer), "%.2lf", rounded);return buffer;
}
// 应用运算符进行计算
double apply_op(double left, double right, const string& op) {if (op == "+") return left + right;if (op == "-") return left - right;if (op == "*") return left * right;if (op == "/") return left / right;return 0;
}
// 递归下降解析表达式
pair<double, int> parse(const vector<string>& tokens, int idx) {if (tokens[idx] == "(") {auto left_result = parse(tokens, idx + 1);  // 解析左表达式double left_val = left_result.first;int pos = left_result.second;string op = tokens[pos];  // 获取运算符pos++;auto right_result = parse(tokens, pos);  // 解析右表达式double right_val = right_result.first;pos = right_result.second;if (pos < tokens.size() && tokens[pos] == ")") {pos++;  // 跳过右括号}return {apply_op(left_val, right_val, op), pos};} else {return {str_to_double(tokens[idx]), idx + 1};  // 数字直接转换}
}
int main() {int N;cin >> N;cin.ignore();  // 忽略换行符for (int i = 0; i < N; i++) {string line;getline(cin, line);vector<string> tokens = tokenize(line);  // 词法分析auto result = parse(tokens, 0);  // 语法分析并求值cout << double_to_str(result.first) << endl;  // 格式化输出}return 0;
}

关键点总结

  1. 词法分析:正确处理负号与减号的区分是解题的关键
  2. 递归下降分析:利用表达式完全括号化的特点,简化语法分析
  3. 精度处理:使用 double\texttt{double}double 进行计算,输出时进行四舍五入
  4. 边界情况:特别注意负零 −0.00-0.000.00 的输出格式要求

该解法能够正确处理各种复杂的嵌套表达式,满足题目的所有精度和格式要求。

http://www.dtcms.com/a/490437.html

相关文章:

  • json转excel xlsx文件
  • 【C++】深入理解string类(5)
  • 六、Hive的基本使用
  • 铜陵网站建设推广江苏核酸检测机构
  • 电子商务网站建设含义如果做车站车次查询的网站需要什么消息信息
  • 【JETSON+FPGA+GMSL】实测分享 | 如何实现激光雷达与摄像头高精度时间同步?
  • 建网站权威公司dw怎么做打开网站跳出提示
  • 阅读:REACT: SYNERGIZING REASONING AND ACTING INLANGUAGE MODELS(在语言模型中协同推理与行动)
  • 语义三角论对AI自然语言处理中深层语义分析的影响与启示
  • SpringBoot 启动时执行某些操作的 8 种方式
  • Cloud IDE vs 本地IDE:AI编程时代的“降维打击“
  • RocketMQ 事务消息
  • 做网站的不肯给ftp企业163邮箱登录
  • reactNative 遇到的问题记录
  • 使用 Cloudflare Turnstile 实现 Java 后端的人机验证
  • 【论文阅读】Knowledge Circuits in Pretrained Transformers
  • SpringBoot3集成Mybatis(开启第一个集成Mybatis的后端接口)
  • 论文阅读 (2) :Reducing Divergence in GPGPU Programs with Loop Merging
  • React 01
  • 建设开发网站潍坊百度网站优化
  • AI 在数据库操作中的各类应用场景、方案与实践指南
  • ASTM C615/C615M-23 花岗石检测
  • 用php做的网站论文抖音的商业营销手段
  • 子数组/子串问题
  • 办公空间设计网站浙江恒元建设网站
  • 银河麒麟 aarch64 linux 里面的 qt 怎么安装kit
  • 2025电脑价格数据集/构建电脑价格预测模型/数据量为 10 万行
  • Linux 系统下 MySQL 的安装配置
  • 16、Docker Compose 安装Kafka(含Zookeeper)
  • QT(c++)开发自学笔记:2.TCP/IP