P4711 「化学」相对分子质量
月赛的经典模拟题
模拟水题调到要死,但是还是能1A,还是很爽的。
这道题可以直接用scanf读入一行,用fgets会读入一个'\n'
,我不喜欢。
然后用一个变量从左到右扫一遍。
如果读到了大写字母,那么你现在肯定在读一个元素的名称。判断是否有小写字母,名字完了之后判断是不是有下标数字。
读数字是一件挺频繁的工作,建议封装成函数,很爽。
有一个难点是原子团,不过不难发现:用括号括起来的原子团一定会有下标!这是常识
处理原子团的时候应该单独计局部重量,到最后读完整个原子团的时候再加到总答案里面去。
最后的结晶水只需要读个数字走人就好了。如果没数字就根本不用读就是1。
没有数字默认的量就是1,这个最好设置着保险。
多debug看一下程序怎么跑,有时就能发现你的逻辑错误所在。
代码:
#include<cstdio>
#include<cstring>
#include<utility>
const int maxn = 105;
char ch[maxn];
int len;
double mass(char ch1, char ch2)
{
if(ch1 == 'H' && ch2 == 0) return 1;
else if(ch1 == 'C' && ch2 == 0) return 12;
else if(ch1 == 'N' && ch2 == 0) return 14;
else if(ch1 == 'O' && ch2 == 0) return 16;
else if(ch1 == 'F' && ch2 == 0) return 19;
else if(ch1 == 'N' && ch2 == 'a') return 23;
else if(ch1 == 'M' && ch2 == 'g') return 24;
else if(ch1 == 'A' && ch2 == 'l') return 27;
else if(ch1 == 'S' && ch2 == 'i') return 28;
else if(ch1 == 'P' && ch2 == 0) return 31;
else if(ch1 == 'S' && ch2 == 0) return 32;
else if(ch1 == 'C' && ch2 == 'l') return 35.5;
else if(ch1 == 'K' && ch2 == 0) return 39;
else if(ch1 == 'C' && ch2 == 'a') return 40;
else if(ch1 == 'M' && ch2 == 'n') return 55;
else if(ch1 == 'F' && ch2 == 'e') return 56;
else if(ch1 == 'C' && ch2 == 'u') return 64;
else if(ch1 == 'Z' && ch2 == 'n') return 65;
else if(ch1 == 'A' && ch2 == 'g') return 108;
else if(ch1 == 'I' && ch2 == 0) return 127;
else if(ch1 == 'B' && ch2 == 'a') return 137;
else if(ch1 == 'H' && ch2 == 'f') return 178.5;
else if(ch1 == 'P' && ch2 == 't') return 195;
else if(ch1 == 'A' && ch2 == 'u') return 197;
else if(ch1 == 'H' && ch2 == 'g') return 201;
else return 0;
}
std::pair<int,int> read(int pos)
{
int ans = 0;
while(pos < len)
{
if(ch[pos] < '0' || ch[pos] > '9') break;
ans = ans * 10 + ch[pos] - '0';
pos++;
}
return std::make_pair(ans, pos);
}
double solve()
{
len = strlen(ch);
double total_weight = 0;
bool in_yuanzituan = false;
double yuanzituan_weight = 0;
char name[2] = {0}; int num = 1;
for(int i = 0; i < len; )
{
int pos = i;
if(ch[pos] >= 'A' && ch[pos] <= 'Z')
{
// 开始读入元素名
name[0] = ch[i];
num = 1;
if(ch[i + 1] >= 'a' && ch[i + 1] <= 'z')// 两个字母的
{
name[1] = ch[i + 1];
if(ch[i + 2] == '_')
{
std::pair<int,int> pii = read(i + 4);
num = pii.first;
i = pii.second + 1;
}
else i += 2;
}
else if(ch[i + 1] == '_')// 一个字母但有数字的
{
name[1] = 0;
std::pair<int,int> pii = read(i + 3);
num = pii.first;
i = pii.second + 1;
}
else// 一个字母还没数字
{
name[1] = 0;
i++;
}
//结算
if(in_yuanzituan) yuanzituan_weight += mass(name[0], name[1]) * num;
else total_weight += mass(name[0], name[1]) * num;
}
else if(ch[pos] == '(')
{
in_yuanzituan = true;
i++;
}
else if(ch[pos] == ')')
{
std::pair<int,int> pii = read(pos + 3);
num = pii.first;
i = pii.second + 1;
total_weight += yuanzituan_weight * num;
in_yuanzituan = false;
yuanzituan_weight = 0;
}
else if(ch[pos] == '~')// 后面是水合物
{
num = 1;
if(ch[pos + 1] >= '0' && ch[pos + 1] <= '9')
{
num = read(pos + 1).first;
}
total_weight += 18 * num;
break;
}
}
// how to do?
return total_weight;
}
int main()
{
scanf("%s", ch);
double ans = solve();
int temp = ans;
if(ans - temp < 1e-5) printf("%.0lf\n", ans);
else printf("%.1lf\n", ans);
return 0;
}