【题解】Luogu[P4711] 「化学」相对分子质量
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;
}