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

· · 题解

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

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

1.元素

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

2.下标

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

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

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

3.原子团

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

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

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

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

4.水合物

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

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

#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;
}