P4711 「化学」相对分子质量(简单易懂 且带拓展版本)

题目描述

做化学题时,小 F 总是里算错相对分子质量,这让他非常苦恼。

小 F 找到了你,请你来帮他算一算给定物质的相对分子质量。

如果你没有学过相关内容也没有关系,你可以从样例和提示里理解该题所求内容。

输入格式

输入一行,为一个长度为 L (L≤100) 的不含空格的字符串,表示给定物质的化学式。

化学式仅包括以下内容:

  • 元素:如 Au(金),Hf(铪),出现的所有元素及其相对原子质量以附表为准
  • 下标 _{} :表示某个原子、离子或者原子团的个数,如 H_{2}O 表示 H2O (水),C_{60} 表示 C60 (足球烯)。
  • 括号 ():表示一个原子团,下标对团内物质生效。如 Ca(OH)_{2} 表示 Ca(OH)2​ (熟石灰)。
  • 水合物 ~:如 CuSO_{4}~5H_{2}O 表示 CuSO4⋅5H2O(胆矾)。水之前如果存在数字,保证一定是大于 22 的正整数,如果省略该部分则默认为 1 。如上述胆矾中,表示水的个数的 5

形式化地讲,你处理的化学式满足以下规则:

分子~数量H_{2}O

其中数量或水合部分可省。

对于分子,满足:

部分_{数量}部分_{数量}...部分_{数量}

其中数量可省。

对于每个“部分”(原子,原子团,离子……),满足:

元素

(元素_{ 数量 }元素_{ 数量}... 元素_{数量})

其中数量可省。

请注意,满足上述条件的化学式不会出现括号嵌套;上文中出现的 “数量” 所指代的数字不超过 10000。

输出格式

输出一行,包含一个整数或者小数部分为 .5 的实数,为你的计算结果。

保证结果不超过 10000。

输入输出样例

输入 #1

Au

输出 #1

197

输入 #2

HfO_{2}

输出 #2

210.5

输入 #3

Ca(OH)_{2}

输出 #3

74

输入 #4

CuSO_{4}~5H_{2}O

输出 #4

250

输入 #5

KAl(SO_{4})_{2}~12H_{2}O

输出 #5

474

解题思路(小tips)

首先 明确题目中的要求:即在字符串的读取中处理如下几种情况

1.遇到普通元素字符时,直接利用存下的unordered_map键值映射 得到对应元素值

只要注意有些元素是双元素 但仔细观察会发现 双元素都是一个大写字母+一个小写字母 因此只要进行一次if判断即可

2.()遇见括号 就将里面的元素当作一个整体单位来计算值 并且看见这里立刻就想到用栈 

3._{}遇见此字符串 则将前面的括号或元素的值取出 乘括号中的数值 再存进栈中

4.最后则是在遇到~nH_{2}O时,因为题目中明确表示最后一定是水 所以只需要读取出n的数值 直接乘以18存进栈中即可 并且因为一定出现在最后 所以计算完就直接break结束 防止再次计算到后面的H_{2}O (ps:这里需要注意 题目中写明当没有~nH_{2}O部分时 是默认n=1,最后还要再次加上18,此处容易忽略 即测试点12不通过)

AC代码展示

在这里给大家提供两种思路:

1.针对此题 无括号嵌套且无双字母元素带下标的多次计算 如无(SAu_{4})_{2}

2.可计算多重嵌套且双字母元素下标

如果觉得有用 就点赞+收藏 鼓励一下吧

本题版本

#include <iostream>
#include <stack>
#include <unordered_map>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
stack<double> s;
unordered_map<string,double> mp;
string str;
double temp;
void prepare(){
	mp["H"]=1,mp["C"]=12,mp["N"]=14,mp["O"]=16,mp["F"]=19,mp["Na"]=23,
	mp["Mg"]=24,mp["Al"]=27,mp["Si"]=28,mp["P"]=31,mp["S"]=32,mp["Cl"]=35.5,
	mp["K"]=39,mp["Ca"]=40,mp["Mn"]=55,mp["Fe"]=56,mp["Cu"]=64,
	mp["Zn"]=65,mp["Ag"]=108,mp["I"]=127,mp["Ba"]=137,mp["Hf"]=178.5,
	mp["Pt"]=195,mp["Au"]=197,mp["Hg"]=201;
}

double check(int &i){
	string s1="";
	s1+=str[i];
	if(isupper(str[i])&&islower(str[i+1])){
		i++;
		s1+=str[i];
	}
	//cout<<mp[s1]<<endl;
	return mp[s1];
}
double check2(int &i){
	int temp=0;
	i+=2;
	double t=s.top();
	s.pop();
	while(isdigit(str[i])) {
		temp=temp*10+str[i]-'0';
		i++;
	}
	return t*temp;
}
int main(){
	IOS;
	prepare();
	cin>>str;
	for(int i=0;i<str.size();i++){
		temp=0;
		if(isalpha(str[i])){
			temp=check(i);
			s.push(temp);
		} 
		else if(str[i]=='('){
			i++;
			while(str[i]!=')') {
				int tmp=check(i); 
				temp+=tmp;
				if(str[i+1]=='_') {
					i++;
					temp-=tmp;
					s.push(tmp);
					temp+=check2(i);
				}
				i++;
			}
			s.push(temp);
		}
		else if(str[i]=='_'){
			s.push(check2(i));
		}
		else if(str[i]=='~'){
			i++;
			if(!isdigit(str[i])) temp=1;
			while(isdigit(str[i])) {
				temp=temp*10+str[i]-'0';
				i++;
			}
			s.push(temp*18);
			break;
		}
	}
	double sum=0;
	while(!s.empty()){
		sum+=s.top();
		s.pop();
	}
	cout<<sum<<endl;
	return 0;
} 

高级版

#include <iostream>
#include <stack>
#include <unordered_map>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
stack<double> s;
unordered_map<string,double> mp;
string str;
double temp;
void prepare(){
	mp["H"]=1,mp["C"]=12,mp["N"]=14,mp["O"]=16,mp["F"]=19,mp["Na"]=23,
	mp["Mg"]=24,mp["Al"]=27,mp["Si"]=28,mp["P"]=31,mp["S"]=32,mp["Cl"]=35.5,
	mp["K"]=39,mp["Ca"]=40,mp["Mn"]=55,mp["Fe"]=56,mp["Cu"]=64,
	mp["Zn"]=65,mp["Ag"]=108,mp["I"]=127,mp["Ba"]=137,mp["Hf"]=178.5,
	mp["Pt"]=195,mp["Au"]=197,mp["Hg"]=201;
}

double check(int &i){
	string s1="";
	s1+=str[i];
	if(isupper(str[i])&&islower(str[i+1])){
		i++;
		s1+=str[i];
	}
	return mp[s1];
}
double check2(int &i){
	int temp=0;
	i+=2;
	double t=s.top();
	s.pop();
	while(isdigit(str[i])) {
		temp=temp*10+str[i]-'0';
		i++;
	}
	return t*temp;
}
int main(){
	IOS;
	prepare();
	cin>>str;
	for(int i=0;i<str.size();i++){
		temp=0;
		if(isalpha(str[i])){
			temp=check(i);
			s.push(temp);
		}
		else if(str[i]=='_'){
			s.push(check2(i));
		}
		else if(str[i]=='('){
			s.push(-1);
		}
		else if(str[i]==')'){
			double sum=0;
			while(s.top()!=-1){
				sum+=s.top();
				s.pop();
			}
			s.pop();
			s.push(sum);
		} 
		else if(str[i]=='~'){
			i++;
			if(!isdigit(str[i])) temp=1;
			while(isdigit(str[i])) {
				temp=temp*10+str[i]-'0';
				i++;
			}
			s.push(temp*18);
			break;
		}
	}
	double sum=0;
	while(!s.empty()){
		sum+=s.top();
		s.pop();
	}
	cout<<sum<<endl;
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值