【题解】Luogu[P4711] 「化学」相对分子质量

发布时间 2023-07-29 13:28:58作者: AgrumeStly

Link

一道简单的模拟题,评绿可能有点高了。

因为没有括号嵌套,难度瞬间降了一个档次,我们直接对着化学式扫一遍即可。

若扫到左括号,说明接下来都是在括号内的,我们标记一下。

若扫到大写字母,说明出现了一个新的元素,那么我们就看后面是否有下标,若有则类似于快读的方式直接处理后面的数字,然后计算当前元素的相对质量。若此时这个元素不在括号内,那么我们直接加入到总答案中;若在括号内,那么接着计算括号内的元素相对质量的总和。

若扫到大括号,则用之前同样的方法计算大括号的下标,用之前记录的括号内元素相对质量总和乘上下标,再计入总答案中。

若扫到 ~,说明后面有个水合物,我们可以直接看水之前是否存在数字,若存在,用之前的方法算出数量,若不存在,数量默认为 \(1\)。然后直接拿水的相对分子质量 \(18\) 乘以水的质量,加到总答案中。我们知道水合物后面一定没有东西了,于是我们可以在计算完水合物后直接结束循环,最后输出答案即可。

需要注意的是在扫的过程中对当前下标位置的细节处理,这里容易出错。

#include <bits/stdc++.h>
using namespace std;
string str;
int n;
double ans;
map< string, double > m;
void init() {
    m["H"] = 1, m["C"] = 12, m["N"] = 14, m["O"] = 16, m["F"] = 19, m["Na"] = 23, m["Mg"] = 24, m["Al"] = 27, m["Si"] = 28, m["P"] = 31, m["S"] = 32, m["Cl"] = 35.5, m["K"] = 39, m["Ca"] = 40, m["Mn"] = 55, m["Fe"] = 56, m["Cu"] = 64, m["Zn"] = 65, m["Ag"] = 108, m["I"] = 127, m["Ba"] = 137, m["Hf"] = 178.5, m["Pt"] = 195, m["Au"] = 197, m["Hg"] = 201;
}
bool isNum(char ch) { return ('0' <= ch && ch <= '9'); }
bool isCaps(char ch) { return ('A' <= ch && ch <= 'Z'); }
bool uisCaps(char ch) { return ('a' <= ch && ch <= 'z'); }
int getNum(int &i) {
    int res = 0;
    while(i <= n && isNum(str[i])) res = res * 10 + (str[i] - '0'), ++i;
    return res;
}
int main() {
    cin >> str, n = str.size(), str = "?" + str, init();
    int num = 0; double res = 0; string a; bool isf = false;
    for(int i = 1; i <= n; ++i) {
        num = 0, a.clear();
        if(str[i] == '(') isf = true;
        if(isCaps(str[i])) {
            a += str[i];
            while(i + 1 <= n && uisCaps(str[i + 1])) ++i, a += str[i];
            if(str[i + 1] == '_') i += 3, num = getNum(i);
            else num = 1;
            res += m[a] * (double)num;
            if(!isf) ans += res, res = 0;
            num = 0;
        }
        if(str[i] == ')') i += 3, num = getNum(i), res *= (double)num, ans += res, res = num = 0, isf = false;
        if(str[i] == '~') {
            ++i, num = 0;
            if(isNum(str[i])) num = getNum(i);
            else num = 1;
            ans += (double)num * 18.0; break;
        }
    }
    cout << ans << endl;
    return 0;
}