首页 > 其他分享 >【题解】P4711 「化学」相对分子质量

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

时间:2024-05-03 15:22:39浏览次数:21  
标签:分子 原子团 题解 else num P4711 mp && LRbegin

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

标签:分子,原子团,题解,else,num,P4711,mp,&&,LRbegin
From: https://www.cnblogs.com/Daniel-yao/p/18171188

相关文章

  • [题解]ABC337E Bad Juice
    ABC337EBadJuice一开始的想法如下:就是利用二分法,对于一个区间\([l,r]\),分成\([l,mid-1],[mid,r-1]\)两部分,各找两个朋友喝,右边还空出一个\(r\),如果前面两个朋友都没中毒,那说明\(r\)这瓶有毒。但仔细一想,我们发现\([1,n)\)的瓶子中任意一个我们分出的区间\([l,r]\),都用去了\(......
  • 题解:CF607E Cross Sum
    Problem给定\(N\)条不平行的直线\(y=\frac{k_i}{1000}x+\frac{b_i}{1000}\),\(N\)条直线总共会有\(\frac{N(N-1)}{2}\)个交点(包含在同一个位置的点,即相同位置算不同的点),找出距离原点前\(K\)近的交点的距离和。$2\leN\le5\times10^4$,\(1\leK\le\frac{N(N-1)}{2}\)......
  • P6123 [NEERC2016] Hard Refactoring 题解
    本题说白了,就是一道big模拟!!!题意不再赘述,我们直接看思路。这里作者借鉴了某差分思想:末尾加空格,用于判断最后一个条件;若只有\(\le\),对给出的数字和数组第一个进行标记。标记的时候要+32769,因为数组中不存在负数下标,以免越界;若只有\(\ge\),就标记给出的数字和数组最后......
  • 5.2考试题解
    T1[NOIP2017提高组]时间复杂度大模拟……#include<bits/stdc++.h>#definelllonglongusingnamespacestd;intt,n,k,as,nw,tr,ed[105];intc[26],str[105],b[105];stringtim;stack<int>st;structAadd{strings,t,fr,ed;}ad[105];intdfs(intx){i......
  • P4921 题解
    linkHint:错排计数。\(ans_k=C_n^k\timesA_n^k\times2^k\timesg(n-k)\)\(g(i)\)代表\(i\)对情侣全部错开的方案数。解释一下\(ans_k\)的表达。我们从\(n\)对情侣中无序地抽出\(k\)对情侣,有序地放到\(k\)排座位上,这里的方案数是\(C_n^k\timesA_n^k\)。由于两个......
  • UVA1362 Exploring Pyramids 题解
    题目传送门前置知识欧拉序|区间DP|乘法原理解法DFS序可近似理解为欧拉序,故考虑区间DP。设\(f_{l,r}\)表示\([l,r]\)对应的二叉树的个数,状态转移方程为\(f_{l,r}=\begin{cases}1&l=r\\[s_{l}=s_{r}]\times\sum\limits_{i=l+2}^{r}[s_{l}=s_{i}]\timesf_{......
  • Educational Codeforces Round 165 (Rated for Div. 2) C. Minimizing the Sum题解
    题意CodeforcesRound809(Div.2)D1.ChoppingCarrots(EasyVersion)给你两个整数\(n(1\len\le3e5),k(0\lek\le10)\),一个数组\(a(1\lea_i\le10^9)\)。你可以进行如下操作最多\(k\)次:选定一个数\(i(1\lei\len)\),让其变为相邻的数(变为\(a_{i-1},a_{i......
  • P1017 [NOIP2000 提高组] 进制转换 题解
    题目简述给定一个十进制数$n$,将其转换成一个$-R$进制数。题目分析十进制数转负进制,同样可以使用短除取余法,但是会出现余数为负的情况,例如$-11\div-2=5~\cdots\cdots-1$,此时我们可以用如下法方解决此问题:我们设被除数为$a$,除数为$b$,余数为$c$,商为$d$,其中$c<0$......
  • P2192 HXY玩卡片 题解
    题目简述给定一些$5$和$0$的数字,让你在其中选择一些数进行排列成为一个非负整数,使得这个数字能被$90$整除,且是所有满足条件的数中最大的一个,无解输出$-1$。题目分析如果一个数能被$90$整除,那么它一定能被$9$和$10$整除。能被$10$整除,那就说面答案的个位数一定......
  • [题解]P4597 序列 sequence
    P4597序列sequence是CF13CSequence的加强版,\(N\leq5*10^5\)。如果想了解\(O(N^2)\)的DP解法请看此文。给定\(N\)个数,每次操作可以选其中一个数\(+1\)或\(-1\)。请问要让这个数列不降,最少需要多少次操作?看到数据范围发现不能用\(O(N^2)\)的dp了,需要换一种思路。我们用类......