CCF-CSP题解 201912-3 化学方程式答案与解析![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/dead8a8d9a72979ddd568ef2dcfe9091.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/628db7218043c68136317626aa65d1ee.png)
题目分析
- 该题的目的是让我们求一个化学方程式是否已配平
- 求一个化学式是否配平最简单的方法就是求等号两边原子的数目是否相等
- 每个原子是由一个有且只有一个大写字母开头的英文字符串
- 要求等号两边原子数目是否相等,首先需要把方程式分成左右两边,并且拆分成单个的化学式
- 然后对每个化学式进行分析,将元素名和元素数目存入左右两个map中。
- 最后比较两个map是否相等即可。
- 该题的难点在于怎么计算括号的嵌套,对于这个问题,我们可以采用递归的方法,把括号右侧的下标作为系数传递给子化学式,这样子化学式在计算自己原子的数目时只需要乘上这个系数即可。
算法的核心原理具体体现在如下几个方面:
-
将左右两边的方程式拆分成一个个化学式。
-
计算每个化学式中原子数目
-
对于括号嵌套,采用递归方式解决。
-
将原子名称和其数目以键值对的形式存储在哈希表中,以提高查询速度。
-
将化学式前的系数和化学式后的下标作为递归函数的参数逐级向下传递。
-
最后比较两个哈希表是否相等来判断化学式是否正确。
各函数功能:
函数名称 | get_coef() | count_atom() | build_map() |
---|---|---|---|
功能 | 获取化学式中系数或下标的值,该函数执行完毕后,会将传入的索引移至系数之后。 | 计算所给化学式的原子数目并存入所给哈希表 | 计算所给化学方程式一侧(不含等号)中的原子数目并存入所给哈希表。 |
一、 算法主要流程
程序总体流程:
-
对于每组测试用例,输入化学方程式,并保存在字符串formula中。
-
寻找字符串中’=’所在位置,将化学方程式分为左右两侧。
-
对化学方程式中每一侧调用build_map(),分别传入左右两个哈希表le, re。
-
build_map()会扫描传入公式,并根据’+’所在位置截取化学式,并调用get_coef()计算化学式前的系数传给count_atom(),count_atom()函数负责递归计算化学式中原子的数目,并存入哈希表中。
-
get_coef()函数流程:
根据所给起始索引,从左向右扫描化学式并更新索引,如果遇到非数字字符则停止,将扫描过的数字字符串转换为整数返回。
-
count_atom()函数流程:
根据所给字符串起始位置i,终止位置j扫描该化学式,如果遇到大写字母,则说明遇到原子,继续向后扫描直到遇到非小写字母,将扫描过的字符串作为原子的名称,此后继续扫描下标,此操作只需调用一次get_coef()函数即可,然后将get_coef()函数的返回值乘以传入的系数即为该原子的数目,存入哈希表即可。如果遇到左括号,说明遇到子化学式,令k=1,继续扫描,每遇到一个左括号k值加1,遇到一个右括号k值减1,当k值为0时说明子化学式结束。此后只需调用get_coef()函数计算下标并与传入的系数相乘,作为子化学式的系数传入子函数递归计算即可。
-
build_map()函数流程:
该函数根据加号截取每个化学式:即用索引j记录化学式左侧索引,i记录化学式右侧索引,调用get_coef(j)计算化学式系数、调用count_atom()函数,将i和j作为参数传入即可计算每个化学式的原子个数(C++编译器默认使用的是 __cdecl 模式,即参数从右向左读取,因此可以将get_coef(j)直接作为函数参数传入,可以推断其左侧传入的j相应已被改变)。
答案代码
#include <iostream>
#include <unordered_map>
using namespace std;
string formula;
int n;
unordered_map<string, int> lm, rm;
// 获取化学式前面的系数,运行后i会指向系数后化学式的开头
int get_coef(int& i)
{
if (!isdigit(formula[i]))return 1; //化学式前或后没有系数,则只有一个
int coef = atoi(&formula[i]);
while (isdigit(formula[++i])); // 令i指向系数后化学式的开头
return coef;
}
void count_atom(unordered_map<string, int>& mp, int i, int j, int coef)
{
while(i < j)
{
if (formula[i] == '(')
{
i++; int bpos = i; //起始子化学式位置(左括号右侧)
for (int k = 1; i < j; i++)
{
if (formula[i] == '(') k++;
else if (formula[i] == ')') k--;
if (!k)
{
int epos = i; i++; //子化学式结束位置(右括号)
// 处理括号内部
count_atom(mp, bpos, epos, get_coef(i) * coef);
break;
}
}
}
else if (isupper(formula[i])) // 元素名
{
int k = i; while (islower(formula[++i]));
string name = formula.substr(k, i - k);
mp[name] += get_coef(i) * coef;
}
else i++;
}
}
void build_map(unordered_map<string, int>& mp, int start, int end)
{
int j = start;
for (int i = start; i < end; i++)
if (formula[i] == '+')
{
count_atom(mp, j, i, get_coef(j));
j = i + 1;
}
count_atom(mp, j, end, get_coef(j));
}
int main()
{
cin >> n;
while (n--)
{
cin >> formula;
size_t pos = formula.find('=');
build_map(lm, 0, pos);
build_map(rm, pos + 1, formula.size());
if (lm == rm) cout << "Y" << endl;
else cout << "N" << endl;
lm.clear(); rm.clear();
}
}
输入数据
/*
12
H2+O2=H2O
2H2+O2=2H2O
H2+Cl2=2NaCl
H2+Cl2=2HCl
CH4+2O2=CO2+2H2O
CaCl2+2AgNO3=Ca(NO3)2+2AgCl
3Ba(OH)2+2H3PO4=6H2O+Ba3(PO4)2
3Ba(OH)2+2H3PO4=Ba3(PO4)2+6H2O
4Zn+10HNO3=4Zn(NO3)2+NH4NO3+3H2O
4Au+8NaCN+2H2O+O2=4Na(Au(CN)2)+4NaOH
Cu+As=Cs+Au
2B10H12CNH3+NiCl2+6NaOH=Na4(B10H10CNH2)2Ni+2NaCl+6H2O
*/