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

Problem

给定一个长度为 \(L\) 的化学式,求出此化学式的相对分子质量。

例:十二水合硫酸铝钾(明矾)\(KAl(SO_4)_2 \cdot 12H_2O\).

输入格式形为:KAl(SO_{4})_{2}~12H_{2}O

Solve

清新小模拟。

定义一下“结账”这个概念,分为三种:

  1. 原子结账,即为当单独的一个(一坨)原子计算完成后,计入所属的合物的答案;

  2. 原子团结账,即为当单独的原子团计算完成后,计入所属的合物的答案;

  3. 合物结账,即为当合物答案计算完毕后,计入总答案;

定义所用变量名

Endle:bool变量,记录在当前位置是否进行原子结账

EndLR:bool变量,记录在当前位置是否进行原子团结账

LRbegin:bool变量,记录在当前位置是否在原子团回合

ans:int变量,记录总答案;

num:int变量,记录当前原子总账;

nnum:int变量,记录当前原子团总账;

indp:int变量,记录当前合物总账;

mul:int变量,记录当前合物系数;

定义基本函数

bool CheckSmall(char s) {//判断小写
  return (s >= 'a' && s <= 'z');
} 

bool CheckBig(char s) {//判断大写
  return (s >= 'A' && s <= 'Z'); 
} 

bool CheckNumber(char s) {//判断数字
  return (s >= '0' && s <= '9'); 
} 

int get_num(string s) {//字符转数字
  int res = 0;
  for (int i = 0; i < s.size(); i++) {
    res = res * 10 + (s[i] - '0'); 
  }
  return res;
}

1. 处理分子中的原子(原子团)

先要弄一个桶,把题中涉及到的元素的相对原子质量记录下来。这里可以选择使用 map/unordered_map 来当桶。

void init() {
  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;
}

然后分类讨论:

  1. 如果只有单独的一个元素,直接结账;
  2. 要是有一坨(带系数),先计算系数,再乘起来,结账;
if(CheckBig(c[i])) {
  if(CheckSmall(c[i + 1])) {//两个字母表示的元素
    string s = "";
    s += c[i]; s += c[i + 1];
    if(!LRbegin) num += mp[s];//原子账单增加
    nnum += mp[s];//原子团账单增加
    if(c[i + 2] != '_' && !LRbegin) Endel = 1;//无系数,不是原子团,原子结账!
    if(c[i + 2] != '_' && LRbegin) EndLR = 1;//无系数,是原子团,原子团结账!
    i++;
  } else {//一个字母表示的元素
    string s = ""; s += c[i];
    if(!LRbegin) num += mp[s];//原子账单增加
    nnum += mp[s];//原子团账单增加
    if(c[i + 1] != '_' && !LRbegin) Endel = 1;//无系数,不是原子团,原子结账!
    if(c[i + 1] != '_' && LRbegin) EndLR = 1;//无系数,是原子团,原子团结账!
  }
}

遇到原子团:

if(c[i] == '(' || c[i] == ')') {
  if(c[i] == '(') {
    LRbegin = 1;//回合开始
    nnum = 0;//开始记账
  } else {
    LRbegin = 0;//回合结束
  }
}

遇到系数:

if(c[i] == '_') {
  i++; string Num = "";
  while(CheckNumber(c[i + 1])) {
    Num += c[i + 1]; i++;
  }
  i++;
  if(!LRbegin) num *= get_num(Num);//原子系数
  else nnum *= get_num(Num);//原子团系数
  if(!LRbegin) Endel = 1;//不是原子团直接结账
  else {
    LRbegin = 0; EndLR = 1;//强行结束回合,计算系数,结账!
    i++;
  } 
} 

原子(团)结账:(与其是结账,不如叫甩锅qwq)

if(EndLR) {
  num += nnum;//原子团账单丢给原子账单(方便原子团账单继续记账)
  nnum = EndLR = 0;//清算
}
if(Endel) {
  indp += num;//原子账单丢给合物账单
  num = Endel = 0;//清算
}

2. 处理分子中的合物

处理一:在化合物结尾加入 "~" 字符,由此分段标记;
处理二:在此基础上继续在结尾加入 "$" 字符,作为结尾哨兵;

计算合物系数:

if(CheckNumber(c[i])) {
  string Num = "";
  while(CheckNumber(c[i])) {
    Num += c[i];
    i++;
  }
  i--;    
  mul *= get_num(Num);
}

合物结账:

if(c[i] == '~'){
  ans += indp * mul;//合物背下所有的锅,一次付清!
  mul = 1; indp = 0;//清算
}

最后输出答案记得处理整数与小数:

if((int)ans * 10 != ans * 10) printf("%.1lf\n", ans);
else printf("%.0lf\n", ans);

Code

#include <bits/stdc++.h>
#define ll long long
#define H 19260817
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define MOD 1000003
#define mod 1000000007

using namespace std;

namespace Read {
  template <typename T>
  inline void read(T &x) {
    x=0;T f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
  }
  template <typename T, typename... Args>
  inline void read(T &t, Args&... args) {
    read(t), read(args...);
  }
}

using namespace Read;

void print(int x){
  if(x<0){putchar('-');x=-x;}
  if(x>9){print(x/10);putchar(x%10+'0');}
  else putchar(x+'0');
  return;
}

const int N = 1e5 + 10;

char c[N];

int n;

double ans, num, nnum, mul = 1, indp = 0;

unordered_map<string, double> mp;

bool Endel = 0, LRbegin = 0, EndLR = 0;

bool CheckSmall(char s) {
  return (s >= 'a' && s <= 'z');
} 

bool CheckBig(char s) {
  return (s >= 'A' && s <= 'Z'); 
} 

bool CheckNumber(char s) {
  return (s >= '0' && s <= '9'); 
} 

int get_num(string s) {
  int res = 0;
  for (int i = 0; i < s.size(); i++) {
    res = res * 10 + (s[i] - '0'); 
  }
  return res;
}

void init() {
  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;
}

signed main() {
  cin >> (c + 1);
  n = strlen(c + 1);
  init();
  c[n + 1] = '~', c[n + 2] = '$';
  For(i,1,n+1) {
    if(CheckBig(c[i])) {
      if(CheckSmall(c[i + 1])) {
        string s = "";
        s += c[i]; s += c[i + 1];
        if(!LRbegin) num += mp[s];
        nnum += mp[s];
        if(c[i + 2] != '_' && !LRbegin) Endel = 1;
        if(c[i + 2] != '_' && LRbegin) EndLR = 1;
        i++;
      } else {
        string s = ""; s += c[i];
        if(!LRbegin) num += mp[s];
        nnum += mp[s];
        if(c[i + 1] != '_' && !LRbegin) Endel = 1;
        if(c[i + 1] != '_' && LRbegin) EndLR = 1;
      }
    } else if(c[i] == '_') {
      i++; string Num = ""; 
      while(CheckNumber(c[i + 1])) {
        Num += c[i + 1]; i++;
      }
      i++;
      if(!LRbegin) num *= get_num(Num);
      else nnum *= get_num(Num);
      if(!LRbegin) Endel = 1;
      else {
        LRbegin = 0; EndLR = 1;
        i++;
      } 
    } else if(c[i] == '~'){
      ans += indp * mul;
      mul = 1; indp = 0;
    } else if(c[i] == '(' || c[i] == ')') {
      if(c[i] == '(') {
        LRbegin = 1;
        nnum = 0;
      } else {
        LRbegin = 0;
      }
    } else if(CheckNumber(c[i])) {
      string Num = "";
      while(CheckNumber(c[i])) {
        Num += c[i];
        i++;
      }
      i--;    
      mul *= get_num(Num);
    }
    if(EndLR) {
      num += nnum;
      nnum = EndLR = 0;
    }
    if(Endel) {
      indp += num;
      num = Endel = 0;
    }
  }
  if((int)ans * 10 != ans * 10) printf("%.1lf\n", ans);
  else printf("%.0lf\n", ans);
  return 0;
}
posted @ 2024-05-03 15:17  Daniel_yzy  阅读(2)  评论(0编辑  收藏  举报
Title