【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函數
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章