【题解】P4711 「化学」相对分子质量
Problem
给定一个长度为 \(L\) 的化学式,求出此化学式的相对分子质量。
例:十二水合硫酸铝钾(明矾)\(KAl(SO_4)_2 \cdot 12H_2O\).
输入格式形为:KAl(SO_{4})_{2}~12H_{2}O
Solve
清新小模拟。
定义一下“结账”这个概念,分为三种:
-
原子结账,即为当单独的一个(一坨)原子计算完成后,计入所属的合物的答案;
-
原子团结账,即为当单独的原子团计算完成后,计入所属的合物的答案;
-
合物结账,即为当合物答案计算完毕后,计入总答案;
定义所用变量名:
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;
}
然后分类讨论:
- 如果只有单独的一个元素,直接结账;
- 要是有一坨(带系数),先计算系数,再乘起来,结账;
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;
}