[蓝桥杯]小计算器
小计算器
题目描述
模拟程序型计算器,依次输入指令,可能包含的指令有
-
数字:'NUMX',X 为一个只包含大写字母和数字的字符串,表示一个当前进制的数。
-
运算指令:'ADD','SUB','MUL','DIV','MOD',分别表示加减乘,除法取商,除法取余。
-
进制转换指令:'CHANGE K',将当前进制转换为 K 进制(2≤K≤362≤K≤36)。
-
输出指令:'EQUAL',以当前进制输出结果。
-
重置指令:'CLEAR',清除当前数字。
指令按照以下规则给出:
数字,运算指令不会连续给出,进制转换指令,输出指令,重置指令有可能连续给出。
运算指令后出现的第一个数字,表示参与运算的数字。且在该运算指令和该数字中间不会出现运算指令和输出指令。
重置指令后出现的第一个数字,表示基础值。且在重置指令和第一个数字中间不会出现运算指令和输出指令。
进制转换指令可能出现在任何地方。
运算过程中中间变量均为非负整数,且小于 263263。
以大写的'A'~'Z'表示 10~35。
输入描述
第 1 行:1 个nn(1≤n<5×1051≤n<5×105),表示指令数量。
第 2 ⋯n+1⋯n+1行:每行给出一条指令。指令序列一定以'CLEAR'作为开始,并且满足指令规则。
初始默认的进制是十进制。
输出描述
依次输出每一次 'EQUAL' 得到的结果。
输入输出样例
示例
输入
7
CLEAR
NUM 1024
CHANGE 2
ADD
NUM 100000
CHANGE 8
EQUAL
输出
2040
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
总通过次数: 1075 | 总提交次数: 1304 | 通过率: 82.4%
难度: 困难 标签: 2017, 模拟, 国赛
算法思路:状态机与进制转换
本问题需要实现一个支持多进制运算的程序型计算器,核心挑战在于:
- 多进制处理:支持2-36进制转换和数值表示
- 状态管理:需要跟踪当前数值、操作符和进制状态
- 大数运算:中间变量需支持2⁶³范围内的整数运算
- 指令序列处理:按特定规则处理指令序列
采用状态机模型结合进制转换函数实现:
算法步骤
-
初始化状态:
- 当前值
current = 0
- 操作符
op = ""
- 进制
base = 10
- 重置标志
after_clear = true
- 当前值
-
指令处理:
- CLEAR:重置状态,标记下一个NUM为基础值
- NUM X:
- 若在CLEAR后,设为当前值
- 若有待处理操作符,执行运算
- 运算指令:记录操作符,等待操作数
- CHANGE K:更新当前进制
- EQUAL:将当前值转换为当前进制字符串输出
-
进制转换:
- 字符串→数值:按进制解析字符串
- 数值→字符串:除基取余法转换
完整代码实现
#include <iostream>
#include <string>
#include <cctype>
#include <vector>
#include <algorithm>
using namespace std;// 字符串转数值(支持2-36进制)
long long strToNum(const string& s, int base) {long long num = 0;for (char c : s) {int digit = isdigit(c) ? c - '0' : c - 'A' + 10;num = num * base + digit;}return num;
}// 数值转字符串(支持2-36进制)
string numToStr(long long num, int base) {if (num == 0) return "0";string res;bool negative = num < 0;if (negative) num = -num;while (num) {int digit = num % base;res += (digit < 10) ? ('0' + digit) : ('A' + digit - 10);num /= base;}if (negative) res += '-';reverse(res.begin(), res.end());return res;
}int main() {ios::sync_with_stdio(false);cin.tie(0);int n;cin >> n;cin.ignore(); // 清除换行符long long current = 0; // 当前数值string op = ""; // 当前操作符int base = 10; // 当前进制bool after_clear = true; // CLEAR后标记for (int i = 0; i < n; i++) {string command;getline(cin, command);// 指令类型判断if (command == "CLEAR") {current = 0;op = "";after_clear = true;} else if (command.substr(0, 3) == "NUM") {string num_str = command.substr(4);long long num = strToNum(num_str, base);if (after_clear) {current = num;after_clear = false;} else if (!op.empty()) {if (op == "ADD") current += num;else if (op == "SUB") current -= num;else if (op == "MUL") current *= num;else if (op == "DIV") current /= num;else if (op == "MOD") current %= num;op = "";}}else if (command == "ADD") op = "ADD";else if (command == "SUB") op = "SUB";else if (command == "MUL") op = "MUL";else if (command == "DIV") op = "DIV";else if (command == "MOD") op = "MOD";else if (command.substr(0, 6) == "CHANGE") {base = stoi(command.substr(7));}else if (command == "EQUAL") {cout << numToStr(current, base) << endl;}}return 0;
}
代码解析
-
核心函数:
strToNum()
:将字符串按指定进制转换为数值numToStr()
:将数值转换为指定进制的字符串
-
状态管理:
current
:存储当前计算值op
:记录待处理的运算操作after_clear
:标记CLEAR后的特殊状态
-
指令处理流程:
- 使用
getline
读取完整指令 substr
提取指令参数- 状态转换驱动计算过程
- 使用
-
进制转换:
- 支持2-36进制(0-9,A-Z)
- 正确处理负数和零值
实例验证(题目样例)
输入:
7
CLEAR
NUM 1024 // 十进制1024
CHANGE 2 // 转为二进制
ADD // 加法操作
NUM 100000 // 二进制100000 = 十进制32
CHANGE 8 // 转为八进制
EQUAL // 输出结果
处理过程:
CLEAR
:重置状态(current=0, after_clear=true)NUM 1024
:设为当前值(current=1024)CHANGE 2
:进制改为2ADD
:设置操作符为加法NUM 100000
:- 将"100000"从二进制转为十进制:32
- 执行加法:1024 + 32 = 1056
CHANGE 8
:进制改为8EQUAL
:将1056转为八进制 → 2040
输出:2040
✓
注意事项
-
进制边界处理:
- 确保进制K在2-36范围内
- 非法字符检测(如二进制出现'2')
-
数值范围:
- 使用
long long
(64位)存储中间结果 - 检查运算溢出(题目保证<2⁶³)
- 使用
-
特殊运算:
- 除法/取模时除数不能为0
- 负数处理(题目要求非负整数)
-
状态一致性:
- CLEAR后必须重置操作符状态
- 确保NUM指令在正确上下文中执行
多方位测试点
测试类型 | 测试数据 | 预期结果 | 验证要点 |
---|---|---|---|
基本运算 | ADD 后接NUM 5 | 正确求和 | 基本运算功能 |
进制转换 | CHANGE 16 后NUM A | 十进制10 | 字母数字转换 |
连续重置 | 连续CLEAR 指令 | 状态正确重置 | 状态机稳定性 |
边界数值 | NUM 9223372036854775807 | 正确处理 | 大数支持(2⁶³-1) |
非法操作符 | UNKNOWN 指令 | 忽略或报错 | 错误指令处理 |
除数为零 | DIV 后接NUM 0 | 异常处理 | 除零保护机制 |
混合进制运算 | 十进制+二进制值 | 正确结果 | 进制转换一致性 |
长指令序列 | 10⁵条指令压力测试 | 1秒内完成 | 性能优化 |
优化建议
-
预编译指令表:
unordered_map<string, function<void()>> cmd_map = {{"CLEAR", [&](){ /* CLEAR处理 */ }},{"ADD", [&](){ op = "ADD"; }}// ...其他指令 };
-
进制转换优化:
// 使用查表法加速转换 const char digits[36] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z' };
-
运算批处理:
// 支持连续运算(需修改题目规则) if (!op.empty() && !after_clear) {execute_pending_operation(); }
-
错误处理增强:
try {base = stoi(command.substr(7));if (base < 2 || base > 36) throw out_of_range(""); } catch (...) {cerr << "Invalid base: " << command.substr(7) << endl; }
-
内存优化:
// 预留指令存储空间 vector<string> commands; commands.reserve(n);