题目描述
With the given relative atomic mass, calculate the relative molecular mass.
Using simplest dot to represent middle-aligned dot in formula.
输入描述
Single group of test cases.
The first line is two integer n, m. (1 ≤ n ≤ 118, 1 ≤ m ≤ 100)
Next n lines, each line contains a string and a float number. Indicates the atom and the relative atomic mass.
Next m lines, each line contains a formula with atom given before.
Strings contain only letters, numbers, dots and parentheses ‘()’. No square brackets.
输出描述
Print m lines, each line contains an integer, which means the relative molecular mass rounded to integer.
Data promises that there will not be a result with the first decimal place being 5 before round. (Like 316.51)
No fabricate atom
样例输入
5 3
H 1.00794
C 12.0107
O 15.9994
Cu 63.546
S 32.065
CH4
CH3(CH2)4CH3
CuSO4.5H2O
样例输出
16
86
250
题解
这题是英文题面,题目大意是,已知元素的相对原子质量,计算给定化学式的相对分子质量。 此题其实和数学表达式的解析大同小异。
可以发现,对于一个简单的没有括号的化学式,可以从前往后依次扫描,每解析出一个化学元素,确定了它的个数,就在答案中累加它的贡献,遇到点号(如CuSO4.5H2O中的“.”),需要先解析出点号后面的数字(如果没有数字,默认该数为1),最终与后面化学式的相对分子质量相乘,累加进答案。
如果化学式中有括号,就意味着括号里嵌套着另外一个化学式,我们可以把括号内的化学式看做一个整体,使用递归下降法解析它。这样,我们就完成了对给定化学式的相对分子质量的计算。
在实现时要注意充分利用模块化的思想,定义多个函数,包括对化学元素的识别、对数字的识别、对括号的处理等功能函数,以达到复用代码,减少代码量的目的。实现的代码如下:
#include <iostream>
#include <string>
#include <unordered_map>
#include <cctype>
using namespace std;
typedef long long LL;
int n, m, len;
unordered_map<string, double> hmp;
string molecular;
double calcOne(int& );
int getNum(int& st) {
int ans = 0;
while (st < len && isdigit(molecular[st]))
ans = ans * 10 + molecular[st++] - '0';
return ans;
}
double getOneAtomMass(int& st) {
string tmp = "";
tmp += molecular[st++];
if (st < len && islower(molecular[st]))
tmp += molecular[st++];
return hmp[tmp];
}
double processParentheses(int& st) {
double ans = 0;
while (molecular[st] != ')') {
++st;
ans += calcOne(st);
}
++st;
return ans;
}
double calcOne(int& st) {
int num = 1;
if (isdigit(molecular[st])) num = getNum(st);
double tot = 0;
while (st < len && molecular[st] != '.' && molecular[st] != ')') {
int cnt = 1;
double am;
if (isupper(molecular[st])) {
am = getOneAtomMass(st);
if (st < len && isdigit(molecular[st]))
cnt = getNum(st);
} else if (molecular[st] == '(') {
am = processParentheses(st);
if (st < len && isdigit(molecular[st]))
cnt = getNum(st);
}
tot += cnt * am;
}
return num * tot;
}
void solve() {
len = molecular.size();
int st = 0;
double ans = 0;
while (st < len) {
ans += calcOne(st);
++st;
}
printf("%d\n", (int)(ans + 0.5));
}
int main() {
cin >> n >> m;
double mass;
string atom;
for (int i = 0; i < n; ++i) {
cin >> atom >> mass;
hmp[atom] = mass;
}
for (int i = 0; i < m; ++i) {
cin >> molecular;
solve();
}
return 0;
}
总结
这道题还是比较有新意的,感觉需要具备一定的知识迁移能力。该题最关键的一点在于使用递归下降法处理化学式中的括号,递归下降也是数学表达式解析、编程语言语法解析、正则表达式解析的核心所在,非常适合用在对一部分具备递归结构的对象的解析上。
这本是一道我在5月份AC的题目,这篇题解出现得有点迟,一部分说明可能不太到位,如果你看了感觉一头雾水,可以跳过文字,琢磨一下代码的逻辑。