CCF 201912-3 化学方程式

题目

 参考题目 洛谷 2382 化学分子式

化学方程式,也称为化学反应方程式,是用化学式表示化学反应的式子。给出一组化学方程式,请你编写程序判断每个方程式是否配平(也就是方程式中等号左右两边的元素种类和对应的原子个数是否相同)。
本题给出的化学方程式由大小写字母、数字和符号(包括等号=、加号+、左圆括号和右圆括号)组成,不会出现其他字符(包括空白字符,如空格、制表符等),化学方程式的格式与化学课本中的形式基本相同(化学式中表示元素原子个数的下标用正常文本,如H2O写成H2O),用自然语言描述如下:

  • 化学方程式由左右两个表达式组成,中间用一个等号三连接,如2H2+O2=2H2O;
  • 表达式由若干部分组成,每部分由系数和化学式构成,部分之间用加号+连接,如2H2+O2、2H2O;
  • 系数是整数或空串,如为空串表示系数为1;
  • 整数由一个或多个数字构成;
  • 化学式由若干部分组成,每部分由项和系数构成,部分之间直接连接,如H2O、CO2、Ca(OH)2、Ba3(PO4)2;
  • 项是元素或用左右圆括号括起来的化学式,如H、Ca、(OH)、(P04);
  • 元素可以是一个大写字母,也可以是一个大写字母跟着一个小写字母,如H、O、Ca。

输入

从标准输入读入数据。
输入的第一行包含一个正整数n,表示输入的化学方程式个数。
接下来n行,每行是一个符合定义的化学方程式。

输出

输出到标准输出。
输出共n行,每行是一个大写字母Y或N,回答输入中相应的化学方程式是否配平。

输入样例1

11
H2+O2=H2O
2H2+O2=2H2O
H2+Cl2=2NaCl
H2+Cl2=2HCl
CH4+2O2=CO2+2H2O
CaCl2+2AgNO3=Ca(NO3)2+2AgCl
3Ba(OH)2+2H3PO4=6H2O+Ba3(PO4)2
3Ba(OH)2+2H3PO4=Ba3(PO4)2+6H2O
4Zn+10HNO3=4Zn(NO3)2+NH4NO3+3H2O
4Au+8NaCN+2H2O+O2=4Na(Au(CN)2)+4NaOH
Cu+As=Cs+Au

输出样例1

N
Y
N
Y
Y
Y
Y
Y
Y
Y
N

提示

1<=n<=100
输入的化学方程式都是符合题目中给出的定义的,且长度不超过1000
系数不会有前导零,也不会有为零的系数化学方程式的任何一边,其中任何一种元素的原子总个数都不超过10^9
在这里插入图片描述

解题思路

首先要清楚系数出现位置的三种情况:
1、整个化学式的首部
2、元素的右部
3、右括号的右部

如32Ba((OH)2(CO3)2)3(暂不考虑化学式的合法性)
我们从系数入手,在第一种情况下,该系数作用于化学式中的所有元素;在第二种情况下,该系数作用于紧接着的左边的元素;在第三种情况下,该系数作用于紧接着的左边的匹配括号里的所有元素,请通过上例理解。
为此,我们考虑使用一个数组将化学式的各部分存储起来arr,实现逻辑如下:
1、顺序遍历化学式
2、计算系数的第1种情况,也就是整个化学式的系数factor,继续遍历。
3、遇到左或右括号时,将左或右括号加入到arr中;遇到大写字母时,获取元素名称,将元素名称加入到arr中;遇到数字时,不存到arr中,根据系数的第2、3种情况相应处理(第1种情况已经在第二步处理完成)。
4、对于系数的第2种情况,此时数组arr的最后一个元素就是元素名称,系数作用于它即可;对于系数的第3种情况,从数组尾部逆序遍历,直到遇到左括号,将系数作用于这个范围中的元素,同时要将这一对匹配括号从数组中删除。
至此处理化学式的过程结束。
对于整个化学方程式,将其从等号两边分开处理。使用两个map分别记录左右两边的元素个数,再进行比较。

以上引自

参考代码

/*1、等式左右分成A、B两串
  2、对A串进行操作(B串同理):
    <1>按‘+’分割成子串ai
    <2>先提取化学计量数,再按大写字母分割元素,最后提取右下角数字 
*/ 
#include<bits/stdc++.h>
#define debug(x) cerr<<#x<<" "<<x<<endl;
using namespace std;
const int N=1e3+5;
int n;map<string,int>js,gs;
char S[N],A[N],B[N];
bool af=0;
inline void Split(){
    char *p1=S,*p2;
    p2=strchr(p1,'=');
    strncpy(A,p1,p2-p1);
    A[p2-p1]=0;
    p1=p2+1;
    strcpy(B,p1);
}
int indexA(int p,char *s){
    for(int i=p+1;s[i];i++){
        if(s[i]>='A'&&s[i]<='Z'){
            return i;
        }
    }
    return -1;
}
void counts(char *s){
    int muln=atoi(s);
    if(!muln) muln=1;
    while(*s>='0'&&*s<='9') s++;
    int nown=0,len=strlen(s);
    char tmp[N];
    for(int i=0,r,ri;s[i];i++){
        if(!(s[i]>='A'&&s[i]<='Z')) continue;
        r=indexA(i,s);
        if(!(~r)) r=len;
        int cct=0;
        for(int j=i;j<r;j++) tmp[cct++]=s[j];tmp[cct]=0;
        ri=cct-1;
        while(tmp[ri]>='0'&&tmp[ri]<='9'&&ri>0) ri--;
        nown=atoi(tmp+ri+1);
        if(!nown) nown=1;
        if(~ri) tmp[cct=ri+1]=0;
        if(!af) js[tmp]+=muln*nown;else gs[tmp]+=muln*nown;
        i=r-1;
    }
}
void dealStr(char *s){
    int xn=0;
    for(int i=0;s[i];i++) if(s[i]=='+') xn++;
    char *p1=s,*p2;char tmp[N];
    for(int i=0;i<xn;i++){
        p2=strchr(p1,'+');
        strncpy(tmp,p1,p2-p1);
        tmp[p2-p1]=0;
        counts(tmp);
        p1=p2+1;
    }
    strcpy(tmp,p1);
    counts(tmp);
}
inline bool judge(){
    if(js.size()!=gs.size()) return 0;
    for(auto i=js.begin(),j=gs.begin();i!=js.end();i++,j++){
        if(i->first!=j->first||i->second!=j->second) return 0;
    }
    return 1;
}
inline void Clear(){
    memset(S,0,sizeof S);
    memset(A,0,sizeof A);
    memset(B,0,sizeof B);
    js.clear();gs.clear();
} 
int main(){
    scanf("%d",&n);
    for(int k=1;k<=n;k++,Clear()){
        scanf("%s",S);
        Split();
        af=0;
        dealStr(A);
        af=1;
        dealStr(B);
        puts(judge()?"Y":"N");
    }
    return 0;
}
60分代码
//100分 
#include<bits/stdc++.h>
using namespace std;
typedef map<string,int> ys;
int n;string s;
void work(ys &sav,int l,int r,int mul=1){
    if(l>r) return ;
    int tl=l;
    if(s[l]=='('){
        for(int tot=1;tot;){
            l++;
            if(s[l]=='(') tot++;
            if(s[l]==')') tot--;
        }
        if(l==r) work(sav,tl+1,r-1,mul);
        else{
            int num=0,nr=l;
            for(l++;isdigit(s[l]);l++) num=num*10+s[l]-'0';
            work(sav,tl+1,nr-1,mul*(num?num:1));
            work(sav,l,r,mul);
        }
    }
    else{
        string str; str+=s[l];
        for(l++;islower(s[l]);l++) str+=s[l];
        if(l>r){sav[str]+=mul;return ;}
        int num=0,nr=l;
        for(;isdigit(s[l]);l++) num=num*10+s[l]-'0';
        work(sav,tl,nr-1,mul*(num?num:1));
        work(sav,l,r,mul);
    }
}
void clac(string str,ys& m){
    stringstream ss(str);string item;
    while(getline(ss,item,'+')){
        int l=0,num=0,len=item.size();
        for(;isdigit(item[l]);l++) num=num*10+item[l]-'0';
        s=item.substr(l,len-l);
        work(m,0,len-l-1,num?num:1);
    }
}
bool judge(ys &left,ys &right){
    if(left.size()!=right.size()) return 0;
    for(auto i=left.begin(),j=right.begin();i!=left.end();i++,j++){
        if(i->first!=j->first||i->second!=j->second) return 0;
    }
    return 1;
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        string str,lstr,rstr;ys left,right;
        cin>>str;
        stringstream ss(str);
        getline(ss,lstr,'=');
        getline(ss,rstr);
        clac(lstr,left);
        clac(rstr,right);
        puts(judge(left,right)?"Y":"N");
    } 
    return 0;
}

 

来几组样例吧

input2:

 1 19 
 2 HCl=ClH
 3 H2O=H2+O2
 4 2H2+O2=2H2O
 5 HCl()=ClH()
 6 Ca(OH)2A3=CaA3O2H2
 7 2Ca(OH)2A3=2CaA3O2H2
 8 Ca(OH(CH)2)2A3=CaA3O2H6C4
 9 Ca(OH(CH)2)2A3=CaA3O2H6C5
10 2Ca(OH)2A3=CaA3O2H2
11 NaHCO3=Na2CO3+H2O+CO2
12 2NaHCO3=Na2CO3+H2O+CO2
13 CuCl2=Cu+Cl2
14 Cu+4HNO3=Cu(NO3)2+2NO2+2H2O
15 3Cu+8HNO3=3Cu(NO3)2+2NO+4H2O
16 Mg+CO2=2MgO+C
17 2Mg+CO2=2MgO+C
18 CuCl2=Cu+Cl2
19 CuSO4+2NaOH=Cu(OH)2+Na2SO4
20 8A(7B(6C(5D(4E(3F(2E2G3)2)3)4)5)2)3=8A(7B(6C(5D(4E(3F(E4G6)2)3)4)5)2)3

output2:

 1 Y
 2 N
 3 Y
 4 Y
 5 Y
 6 Y
 7 Y
 8 N
 9 N
10 N
11 Y
12 Y
13 Y
14 Y
15 N
16 Y
17 Y
18 Y
19 Y

 

!!!
最后一组:
3F(2E2G3)2=3F(E4G6)2
这组样例是不存在在的。
根据题目定义:括号中只能是元素a1+数字b1+元素a2+数字b2+……
不存在b1+a1+b2+a2,即<term>不存在数字打头的形式
唯一存在数字打头的形式是<expr>  
posted @ 2020-01-18 15:52  神犇(shenben)  阅读(710)  评论(0编辑  收藏  举报