题解 P4711 【「化学」相对分子质量】

恩这题其实并没有那么难,根本没有必要搞什么单调栈什么的,所以我来一发很短的题解。

本题的思路就是逐个击破,我的思路是这样的:

1.元素

对于单个元素的处理应该是很简单的,因为我们有强大的map,一个表就结束了。

2.下标

这里我主要分两种情况看:

  • 对原子的下标

只需要保存每个元素的值(last),如果发现下标就把已经加到ans里的last减掉,重新加上last×下标系数就可以了。

  • 对原子团的下标

这个等会会在原子团的部分里解释

3.原子团

这个对于原子团中可能出现的嵌套问题,我选择了递归解决

原子团中的原子跟原子团外的原子是一个解决方法(原子+下标)

然后接下来就是原子团的下标

这个问题需要我们对原子团中的运算结果进行一个tmp的保存,然后在退出函数之前检查外面有没有下标,有的话立即解决。

4.水合物

这个就很简单了,检测到~后直接计算系数,然后乘以18(H_{2}O的值)就可以了

具体的一些坑点会在代码里说

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <bits/stdc++.h>
using namespace std;
double ans,last;//小心别手残打成int了(因为有.5的存在)
string a;
map <string,double> mp;//一个map其实可以帮我们很多忙
inline void start(){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;}//打表
int ys(int k){
string s="";int j=k+1;s+=a[k];
for (;a[j]>='a'&&a[j]<='z';j++)s+=a[j];
ans+=last=mp[s];//存下上一个值,为下标作准备
return j;
}//对单个元素处理:用string提取单个元素,然后直接拿mp映射表搞
int xb(int k){
int j=k+2,s=0;
for (;a[j]>='0'&&a[j]<='9';j++)s=s*10+a[j]-'0';
ans-=last,ans+=last*s;
return j;
}//外部下标
double tmp,tlast;//tmp为括号里的运算结果,tlast为括号里的上一个值
int xbk(int k){
int j=k+2,s=0;
for (;a[j]>='0'&&a[j]<='9';j++)s=s*10+a[j]-'0';
tmp-=tlast,tmp+=tlast*s;
return j;
}//括号里的下标
int ysk(int k){
string s="";int j=k+1;s+=a[k];
for (;a[j]>='a'&&a[j]<='z';j++)s+=a[j];
tmp+=tlast=mp[s];
return j;
}//括号里的元素
inline int kh(int k){
int j=k+1,h=1;//h表示当前未匹配的左括号数
while (h!=0){
if (a[j]==')')h--;//匹配到一个
if (!h)break;
if (a[j]>='A'&&a[j]<='Z'){
j=ysk(j);
if (a[j]=='_')j=xbk(j)+1;
}else if (a[j]=='(')h++,j=kh(j);//又出来一个,递归
}
if (a[j+1]=='_')ans+=tmp,last=tmp,j=xb(j+1);//原子团的下标
tmp=0;//记得归0
return j;
}//括号
void sh(int k){
int j=k+1,f=0;
for (;a[j]>='0'&&a[j]<='9';j++)f=f*10+a[j]-'0';
if (!f)f++;//注意!!!如果f没提取到说明省略了系数,千万别忘了默认为1!
ans+=f*18;
}//水合物
int main(){
start();
cin>>a;
for (int i=0;i<a.size();i++){
if (a[i]>='A'&&a[i]<='Z')i=ys(i)-1;
else if (a[i]=='_')i=xb(i);
else if (a[i]=='(')i=kh(i);
else if (a[i]=='~'){sh(i);break;}
}
printf ("%g",ans);//%g的输出可以省掉多余的0
return 0;
}