CCF-CSP认证 201912-3 化学方程式
题意描述
给出n
个化学方程式 让我们判断化学方程式是否配平
输入输出格式
输入第一行包含一个整数n
表示方程式的数量 接下来是n
行化学方程式
数据规模
n
在[1,100] 之间
算法设计
个人认为 这应该是CSP第三题中最好读懂题意的题目了 就是给几个化学方程式 让我们去判断方程式是否配平 直接上字符串处理 但是说来容易 仔细想一想 我们需要考虑的细节还是不少的 我列出了以下需要解决的问题 以及相应的解决方案
- 如何去判断方程式是平的?
我们建立一张图map<string,gg>
gg
即是long long
用来统计元素出现的次数 方程式等号左边出现的元素使得该元素在图中的计数++ 而右边的元素使得该计数-- 最后遍历 map 只要有元素的个数不为0
即为方程式不平 反之 方程式就是配平的 - 以什么为处理的单位?
在这里我们以一个表达式作为处理的单位 将字符串用+
号分割 分割出来的即是表达式 这里的表达式可以是H2
H20
Cu(OH)2
我们单独编写函数来处理这样的表达式 - 数字的处理?
在处理函数中 开始必须计算整个表达式的总系数 而对于每个元素 它的个数便是总系数乘以元素后面的数字 我们单独编写函数来处理数字 而且 参数是引用类型 方便我们处理下一个表达式 - 括号的处理?
这里便体现了我们以表达式作为处理对象的好处了 比如Cu(OH)2
这里括号包裹的显然也是一个表达式 只不过这个表达式的初始系数等于原来的系数 乘以 括号后面的系数 那我们显然可以递归地调用处理表达式的函数
模拟题比较繁琐 希望读者可以耐心地在这些问题的引导下 自己独立编码实现 调试 解决bug 我在代码里添加了一定的注释 希望对实现有所帮助
c++11代码
#include <bits/stdc++.h>
using namespace std;
using gg = long long;
unordered_map<string, gg> mp;// 全局图
gg computenum(const string& s,gg& start){ // start是数字的开头 显然 计算过后的start 指向的不再是数字
gg res = 0; // 初值为0
while(start<s.size() and isdigit(s[start])){
res = res * 10 + (s[start] - '0');
start++;
}
return (res == 0 ? 1 : res); // 总系数不能是0啊!
}
void deal(const string& s,gg e){ // 开始考虑单个字符的计算 e 是这个表达式的总系数 快慢指针处理字符串
gg i = 0, j; // i是处理字符的起点
e *= computenum(s, i); // 计算总系数
while(i < s.size()){
if(isupper(s[i])){ // 确定是一个元素了
if(i+1 < s.size() and islower(s[i+1])){ // 双原子元素
gg k = i;
i += 2; // 指向元素后面的数字
mp[s.substr(k, 2)] += e * computenum(s,i); // 此时 i以及指向了下一个判断位了
}else{ // 单原子元素
gg k = i;
i += 1;
mp[s.substr(k, 1)] += e * computenum(s, i);
}
}else{ // 不是元素 开始考虑括号 其实 括号的内部 就是一个结果相同的表达式
gg left = i, right = i + 1, num = 1; // 开始寻找括号的右边界 处理区间即为[left+1,right-1]
while(num!=0 and right < s.size()){
if(s[right] == '(')
num++;
else if(s[right] == ')')
num--;
right++;
}
deal(s.substr(left + 1, right - left - 2), e * computenum(s, right));
i = right;
}
}
}
void compute(const string& s,gg exp){ // 把整个表达式进行了分割
gg i, j;
for (i = 0; i < s.size();){
j = s.find('+', i);
if(j == string::npos){
deal(s.substr(i),exp);
i = s.size();
}else{
deal(s.substr(i, j - i),exp); // 第二参数是子串的长度
i = j + 1;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
gg ni;
cin >> ni;
string s;
while (ni--){
cin >> s; // 要处理的化学方程式
auto j = s.find('=');
compute(s.substr(0, j), 1); // 计算左边
compute(s.substr(j + 1),-1); // 计算右边 右边要减去 参数是 -1
for(auto i : mp){
if(i.second !=0){
cout << "N\n";
goto loop;
}
}
cout << "Y\n";
loop:;
mp.clear(); // 图一定要清空!
}
return 0;
}