【CCF CSP历年考试带注释全题解 C++版】201912-3 化学方程式

#include <iostream>
#include <unordered_map>
using namespace std;

unordered_map<string, int> record; //用于记录元素对应的数量
int num = 0; //用于最后验证左右原子数相同

bool recursion(string cur, bool add, int time); //用于更新record中元素对应的数量,cur为当前字符串,add为true时增加元素,反之减少元素,time是倍数

int main() {
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		string cur;
		cin >> cur;
		int equal = 0; //用于记录等号的位置
		while (cur[equal] != '=') equal++; //寻找等号的位置
		record.clear(); //由于需要判断不止一条方程式,record是全局变量,每次循环需要先清空
		num = 0; //等式左边总原子数初始化为0
		recursion(cur.substr(0, equal), true, 1); // 将等号左边的整个式子送入递归,增加record中元素对应的数量,基础倍数为1
		if (!recursion(cur.substr(equal + 1), false, 1) || num != 0) { //将等号右边的整个式子送入递归,减少record中元素对应的数量,基础倍数为1,若返回false,则输出N,反之输出Y;或者等式左右原子数不相等也输出N
			cout << "N" << endl;
		}
		else {
			cout << "Y" << endl;
		}
	}
	return 0;
}

bool recursion(string cur, bool add, int time) {
	if (cur.empty()) return true; //若式子为空,返回true
	int plus = 0; //用于记录第一个加号的位置
	while (plus < cur.size() && cur[plus] != '+') plus++; //第一个加号的位置,若无加号则为式子长度
	if (plus < cur.size()) { //如果含有加号,将式子分为加号左边和加号右边两部分送入递归,下面的所有add值等于传来的add值,表示加元素或减元素(原谅我取变量名偷懒了)
		return recursion(cur.substr(0, plus), add, time) && recursion(cur.substr(plus + 1), add, time); 
	}
	else { //如果不含加号,这就是一个带有显式系数或系数为1的一个化学式
		if (cur[0] >= '0' && cur[0] <= '9') { //带有显式系数时
			int num_over = 1; //用于记录下一个需要判断是否是数字的字符位置
			int ratio = cur[0] - '0'; //记录第一个数字,用作化学式的倍率
			while (cur[num_over] >= '0' && cur[num_over] <= '9') { //直到下一个字符不是数字为止,更新化学式的倍率
				ratio *= 10;
				ratio += cur[num_over++] - '0';
			}
			return recursion(cur.substr(num_over), add, time * ratio); //现在知道了化学式的倍率,直接把后面的东西丢给递归去解决,注意递归倍率要乘上一层的倍率
		}
		else if (cur[0] == '(') { //化学式默认系数为1且第一个字符为左括号时
			int bracket_num = 1; //用于记录左括号的数量
			int right = 1; //用于记录第一个左括号对应的右括号的位置,以下简称对应右括号
			while (bracket_num > 0) { //循环直到找到对应右括号,其实这里可以用stack栈操作,左括号入栈右括号出栈当栈为空时就是对应右括号的位置
				if (cur[right] == '(') bracket_num++;
				else if (cur[right] == ')') bracket_num--;
				right++;
			}
			int temp = right; //用临时变量记录对应右括号位置
			int ratio = 0; //接下来要看对应右括号右边有无数字,倍率先设置为0
			while (right < cur.size() && cur[right] >= '0' && cur[right] <= '9') { //从左到右读出数字
				ratio *= 10; //知道倍率为什么要先设置成0了吧~
				ratio += cur[right] - '0';
				right++;
			}
			if (ratio == 0) ratio = 1; //如果对应右括号右边没有数字,则倍率为1
			//接下来将第一个最外层括号里的内容丢给递归,然后把后面的内容也丢给递归,这两部分内容都可能仍含有括号,但是没关系递归就是干这事的,还要注意第一个递归倍率要乘上一层的倍率
			return(recursion(cur.substr(1, temp - 2), add, time * ratio)) && recursion(cur.substr(right), add, time);
		}
		else { //化学式默认系数为1且第一个字符不为左括号时
			int lower = 1; //用于记录第一个元素后面的位置,用小写字母找是因为元素最多含有1个小写字母且在右边
			while (lower < cur.size() && cur[lower] >= 'a' && cur[lower] <= 'z') lower++; //寻找第一个元素后面的位置
			string temp = cur.substr(0, lower); //取出完整元素符号
			int ratio = 0; //接下来寻找倍率的操作同“对应右括号右边倍率”
			while (lower < cur.size() && cur[lower] >= '0' && cur[lower] <= '9') {
				ratio *= 10;
				ratio += cur[lower] - '0';
				lower++;
			}
			if (ratio == 0) ratio = 1;
			if (add) { //如果add为true,说明是等式左边的式子,则增加对应元素的数量,注意要用这一层倍率乘上上一层的倍率
				record[temp] += time * ratio;
				num += time * ratio; //原子数加
			}
			else { //如果add为false,说明是等式右边的式子,则减少对应元素的数量
				if (!record.count(temp)) return false; // 如果等式左边的化学式不含有等式右边化学式的元素则说明没配平,返回false
				record[temp] -= time * ratio;
				num -= time * ratio; //原子数减
				if (record[temp] < 0) return false; //如果减多了说明没配平,返回false
			}
			return recursion(cur.substr(lower), add, time); //这个元素后面可能仍含有化学式,送入递归,若为空字符串,对应函数开头直接返回true
		}
	}
}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值