问题描述:
题目背景
NCLNCLNCL是一家专门从事计算器改良与升级的实验室,最近该实验室收到了某公司所委托的一个任务:需要在该公司某型号的计算器上加上解一元一次方程的功能。实验室将这个任务交给了一个刚进入的新手ZL先生。
题目描述
为了很好的完成这个任务,ZLZLZL先生首先研究了一些一元一次方程的实例:
4+3x=84+3x=84+3x=8
6a−5+1=2−2a6a-5+1=2-2a6a−5+1=2−2a
−5+12y=0-5+12y=0−5+12y=0
ZLZLZL先生被主管告之,在计算器上键入的一个一元一次方程中,只包含整数、小写字母及+、-、=这三个数学符号(当然,符号“-”既可作减号,也可作负号)。方程中并没有括号,也没有除号,方程中的字母表示未知数。
你可假设对键入的方程的正确性的判断是由另一个程序员在做,或者说可认为键入的一元一次方程均为合法的,且有唯一实数解。
输入格式
一个一元一次方程。
输出格式
解方程的结果(精确至小数点后三位)。
输入输出样例
输入 #1
6a-5+1=2-2a
输出 #1
a=0.750
基本思路:
又碰到了与表达式解析和计算有关的题目,之前碰到的题目是计算一个表达式的值,这里则是计算方程了。
首先我们要有一个宏观的思路,我一看到这道题目的思路如下:
- 以=为分隔,两边的式子应该用同一种方法解析出来。
- 解析的结果分为两种,一种是未知数的系数,还有一种就是常数项;这里我直接用pair来返回了。
- 在得到=两边的返回值后,我们就可以通过简单的除法来求解这个问题了。
现在的问题是怎么求解这个表达式:
有两种思路——我的是每一次处理像+1, -123a这样的子表达式。还有一种就是每次处理一个字符,根据读入的字符来设置各种标记字符(比如符号位啊啥的)
后面一种解法自己看luogu中的习题解答部分,我自己处理子表达式的方法如下:
- 处理第一个子表达式不带符号位的情况(其实这个可以通过之前的初始化来搞定)
- 处理系数为1和-1的未知数的情况,因为这样不显示带1和-1;
- 处理常数和未知数。
AC代码:
#include<bits/stdc++.h>
using namespace std;
char alpha;
pair<int, int> Parse(string s) {
int l = 0;
int r = 0;
// 这个最好还是使用decltype,不然编译器会给出warning
decltype(s.size()) i = 0;
while (i < s.size() && s[i] != '=') {
// 判断符号
int sign;
if (i == 0 && (isdigit(s[i]) || isalpha(s[i]))) { // 第一个字符, 特殊情况
sign = 1;
} else if (s[i] == '+') {
sign = 1; ++i;
} else if (s[i] == '-') {
sign = -1; ++i;
}
// 获得常数或者系数
int co = 0;
bool is_alpha = false; // 判断是常数还是未知数的系数
bool has_digit = false;
// 碰到符号就代表一次搜索结束
while (i < s.size() && s[i] != '=' && s[i] != '-' && s[i] != '+') {
// 如果碰到字母
if (isalpha(s[i])) {
alpha = s[i];
is_alpha = true;
}
// 如果碰到数字
if (isdigit(s[i])) {
has_digit = true;
co = co * 10 + s[i] - 48;
}
++i;
}
// 后续处理
if (!has_digit) co = 1; // 如果一个数字都没碰到,说明系数为1
if (is_alpha) l += co * sign;
else r += co * sign;
}
return {l, r};
}
int main() {
string s;
cin >> s;
auto equal = find(s.begin(), s.end(), '=');
// 以=为分隔,解析两边的字符串
pair<int, int> left = Parse({s.begin(), equal});
pair<int, int> right = Parse({++equal, s.end()});
// 获取各种系数,方便进行计算
int a = left.first - right.first;
int b = right.second - left.second;
double result = double(b) / a;
// 输出(注意防止-0.000这种格式出现)
if (fabs(result) == 0.000) {
printf("%c=0.000\n", alpha);
} else {
printf("%c=%.3f\n", alpha, double(b) / a);
}
return 0;
}
其他经验:
- luogu的编译器warning开的很高,所以你这里int和容器内的size_type比较会有警告(隐式转换),解决方法就是通过decltype(),让编译器来帮你获得相关类型的变量(注意这里不能用auto,因为我们使用0来初始化的)。
- 浮点数是有-0.000这样糟糕的东西存在的,如果想要把-0.000转化为0.000,可以使用fabs函数。