【luogu/字符串】计算器的改良(表达式的解析与计算)

问题描述:

题目背景

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

基本思路:

又碰到了与表达式解析和计算有关的题目,之前碰到的题目是计算一个表达式的值,这里则是计算方程了。

首先我们要有一个宏观的思路,我一看到这道题目的思路如下:

  1. 以=为分隔,两边的式子应该用同一种方法解析出来
  2. 解析的结果分为两种,一种是未知数的系数,还有一种就是常数项;这里我直接用pair来返回了。
  3. 在得到=两边的返回值后,我们就可以通过简单的除法来求解这个问题了。

现在的问题是怎么求解这个表达式:

有两种思路——我的是每一次处理像+1, -123a这样的子表达式还有一种就是每次处理一个字符,根据读入的字符来设置各种标记字符(比如符号位啊啥的)

后面一种解法自己看luogu中的习题解答部分,我自己处理子表达式的方法如下:

  1.  处理第一个子表达式不带符号位的情况(其实这个可以通过之前的初始化来搞定)
  2.  处理系数为1和-1的未知数的情况,因为这样不显示带1和-1;
  3. 处理常数和未知数。

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;
}

其他经验:

  1.  luogu的编译器warning开的很高,所以你这里int和容器内的size_type比较会有警告(隐式转换),解决方法就是通过decltype(),让编译器来帮你获得相关类型的变量(注意这里不能用auto,因为我们使用0来初始化的)
  2.  浮点数是有-0.000这样糟糕的东西存在的,如果想要把-0.000转化为0.000,可以使用fabs函数
发布了137 篇原创文章 · 获赞 19 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43338695/article/details/102872913
今日推荐