这是我的第二篇博客,小学期的程序设计课程设计告一段落,内容是完成201912的CSP认证考题,我完成了前三题。攻克难题还是非常有成就感的,所以发博客纪念一下。最近也是在进行ACM的训练,希望自己能再接再厉,不断提高编程能力。

题目内容:判断化学方程式是否配平

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

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

[样例输入]

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 

[样例输出]

N
Y
N
Y
Y
Y
Y
Y
Y
Y
N

①主要数据结构设计:
map<string,int> leftcnt,rightcnt;

②算法设计:
1、输入化学方程式fangcheng,找到等号的位置,将fangcheng分成左右两部分left和right,对两部分各自进行处理,过程相同。
2、对等号一边进行处理,首先在最前面添加“+”,这样所有含系数的分子式就统一跟在“+”后面,便于进一步对每个分子式进行处理。
3、对每个含系数的分子式,首先提取它的系数xishu,再提取不含系数的分子式fenzishi,然后调用函数void deal(int lr,int
xishu,string fenzi)进行处理。
4、deal函数当中,lr参数代表这是化学方程式的左半部分还是右半部分。提取分子式中所含的所有原子,存入map类型的tempcnt中。然后遍历tempcnt,对每个原子回到原来的分子式中从头到尾查找,每次找到包含该原子的位置以后,从分子式的右端往左端查找将其包含在内的圆括号,提取括号后的后缀下标,以及原子后的后缀下标,由此计算出每个原子的数量。最终再乘上之前提取的系数,加到总的计数器leftcnt或rightcnt当中。
5、最终判断leftcnt和rightcnt两个map是否完全相同,相应地输出“Y”或“N”,就实现了题目的要求。

这道题目的思路其实就是把一个复杂的大问题逐步分解成简单的小问题,然后由小及大,一步步地解决问题,代码当中我也作了详细的注释,总共有30多行注释,我也正是这样逐步将这道题目完成的。思路其实不难想到,但真正写代码实现还是相当有挑战性的,还要充分考虑各种特殊情况。后面的小结中我会写一些自己做题的感悟。

100分代码:

#include <bits/stdc++.h>
using namespace std;
map<string,int> leftcnt,rightcnt;//将等号左右两边所有原子个数分别存入map中进行计数
void deal(int lr,int xishu,string fenzi)//对分子式(去掉前面的系数)处理
{map<string,int> tempcnt;for(int i=0;i<fenzi.length();i++){if(isupper(fenzi[i])){string t;t+=fenzi[i];if(i+1<fenzi.length())if(islower(fenzi[i+1]))t+=fenzi[i+1];tempcnt[t]=1;}//将所有原子存入tempcnt中 }map<string,int>::iterator it;for(it=tempcnt.begin();it!=tempcnt.end();it++){string yuanzi=it->first;int pos=0,sum=0,wz=0;while(pos<fenzi.length()&&wz!=-1){int geshu=1;wz=fenzi.find(yuanzi,pos);if(wz!=-1)if(yuanzi.length()==2||(yuanzi.length()==1&&!islower(fenzi[wz+1])))//排除比如要查找的原子是N,而分子式中是Na的情况 {for(int i=fenzi.length()-1;i>=wz+yuanzi.length();i--){if(fenzi[i]==')'){int flag=1,kuohao=1;for(int j=i-1;j>=0;j--){if(fenzi[j]=='(')kuohao--;else if(fenzi[j]==')')kuohao++;if(kuohao==0){if(j>wz)flag=0;break;}}//判断当前查找到的原子是否在这对括号内 if(flag==1){int houzhui=0;string hz;if(i<fenzi.length()-1)//样例中有如Na(Au(CN)2)最后括号后面没有后缀的分子式{for(int k=i+1;k<fenzi.length();k++){if(fenzi[k]>='0'&&fenzi[k]<='9')hz+=fenzi[k];else break;}for(int k=0;k<hz.length();k++){houzhui*=10;houzhui+=(hz[k]-'0');}//提取右括号后的后缀,用houzhui记录 }if(houzhui==0)houzhui=1;//若右括号后没有后缀,则后缀为1 geshu*=houzhui;//原子个数乘以后缀 }}//从分子式右端往左查找“)” }int hou=0;string h;if(wz+yuanzi.length()<fenzi.length()){for(int k=wz+yuanzi.length();k<fenzi.length();k++){if(fenzi[k]>='0'&&fenzi[k]<='9')h+=fenzi[k];else break;}for(int k=0;k<h.length();k++){hou*=10;hou+=(h[k]-'0');}//提取原子后的后缀,用hou记录 }if(hou==0)hou=1;//若原子后没有后缀,则后缀为1 geshu*=hou;//原子个数乘以后缀 sum+=geshu;}pos=wz+1;}//原子可能在分子式中多次出现,所以要挨个查找 sum*=xishu;if(lr==1)leftcnt[yuanzi]+=sum;else if(lr==2)rightcnt[yuanzi]+=sum;}//对tempcnt中每个原子在原分子式中进行查找和处理return;
}
int main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);int n;cin>>n;while(n--){leftcnt.clear(),rightcnt.clear();//清空map string fangcheng,left,right;cin>>fangcheng;//输入化学方程式 int equal;for(int i=0;i<fangcheng.length();i++)if(fangcheng[i]=='='){equal=i;break;}//找到等号的位置,用equal记录 for(int i=0;i<equal;i++)left+=fangcheng[i];for(int i=equal+1;i<fangcheng.length();i++)right+=fangcheng[i];//将方程式分为左右两部分,分别用left、right保存left.insert(0,"+");//使每个分子式(含系数)前都是“+”,便于处理 for(int i=0;i<left.length();i++){if(left[i]=='+'){int xishu=0,p;string xs;for(int j=i+1;j<left.length();j++){if(left[j]>='0'&&left[j]<='9')xs+=left[j];else{p=j;break;}}for(int j=0;j<xs.length();j++){xishu*=10;xishu+=(xs[j]-'0');}//提取每个分子式前的系数,用xishu记录 if(xishu==0)xishu=1;//若分子式前不含系数,则系数为1 string fenzishi;for(int j=p;j<left.length()&&left[j]!='+';j++)fenzishi+=left[j];//提取不含系数的分子式 deal(1,xishu,fenzishi);}//对每个分子式进行处理 }//处理等号左半边 right.insert(0,"+");//使每个分子式(含系数)前都是“+”,便于处理 for(int i=0;i<right.length();i++){if(right[i]=='+'){int xishu=0,p;string xs;for(int j=i+1;j<right.length();j++){if(right[j]>='0'&&right[j]<='9')xs+=right[j];else{p=j;break;}}for(int j=0;j<xs.length();j++){xishu*=10;xishu+=(xs[j]-'0');}//提取每个分子式前的系数,用xishu记录 if(xishu==0)xishu=1;//若分子式前不含系数,则系数为1 string fenzishi;for(int j=p;j<right.length()&&right[j]!='+';j++)fenzishi+=right[j];//提取不含系数的分子式 deal(2,xishu,fenzishi);}//对每个分子式进行处理 }//处理等号右半边 bool flag=1;if(leftcnt.size()!=rightcnt.size())flag=0;else{map<string,int>::iterator it;for(it=leftcnt.begin();it!=leftcnt.end();it++){map<string,int>::iterator itt;itt=rightcnt.find(it->first);if(itt==rightcnt.end())flag=0;//没找到相应的原子 if(itt->second!=it->second)flag=0;//相应的原子个数不同 }}//最后判断leftcnt和rightcnt是否完全相等,即是否配平 if(flag)cout<<"Y"<<endl;elsecout<<"N"<<endl;}return 0;
}

小结

真的要感叹一句“功夫不负有心人”啊。周一晚上我足足用了有四个小时,按照自己的思路写了两百多行代码,然后还遇到各种各样的问题。我在纸上打草稿,发现只对原程序进行调试还不够,并不能解决一些问题,我又分块编写了多个程序,进行多组样例的测试,分步检验程序能否得到正确的结果。最终竟然真的满分通过了!

前几天我做了一定的思考,但还没有进行写代码的尝试。晚上我静下心来挑战,终于攻克了一道目前而言还比较难的题目。我的思路大致是这样的:输入化学方程式以后,首先找到等号的位置,分成左右两部分,分别处理;它们各自又可以被加号分成几个小部分,每个小部分又可以分成前缀系数及分子式,分子式又能分成原子,当然还带有括号和后缀下标数字。其实就是一个不断地把复杂问题简单化,然后从小到大逐步来解决问题。

我又上网看了一些别人写的题解,看看其他人的思路是怎么样的,有些内容其实看起来还是有些费劲的。一篇经典的题解当中说这道题类似编译原理中的递归下降法,其实思路和我的差不多,不过可以采用层层调用函数的方式,使代码更加简洁清晰。这也是我下一阶段练习所要努力的方向。这道题可以说是我进入大一到现在写过的算法题里最难的题之一了,做出来真的非常有成就感。虽然花的时间有点多,如果是正式考试可能还有一定的困难,但至少以后碰到类似的题目不会再有害怕的想法。

整个做题的过程必须思维缜密,而且要非常细心,很多细节一旦没考虑到就会导致错误。比如原子在分子式中可能多次出现;存在例如要查找的原子是N、而分子式中是Na这样的情况;存在形如Na(Au(CN)2)这样最后括号后面没有后缀下标的分子式;前缀系数及后缀下标不存在时要默认设置为1;处理括号时要判断当前原子是否包含在这对括号当中,等等。从有了完整的思路到最终完成题目还是需要相当有耐心的,这也正是题目的考验所在。

CSP 201912-3 化学方程式 100分相关推荐

  1. csp201912-3 化学方程式 100分

    题意很简单,给定一个化学方程式,判断是否已经配平,是则输出"Y",否则输出"N". 更新:getnum函数的循环在执行前,多加了句清零.就因为少了那句清零,我在 ...

  2. CCF202104-5 疫苗运输(100分题解链接)

    试题编号: 202104-5 试题名称: 疫苗运输 时间限制: 1.0s 内存限制: 512.0MB 问题描述: 问题描述 X 市最近生产了一批疫苗,需要运往各地使用.疫苗的运输是一个困难的问题:既要 ...

  3. CCF201912-3 化学方程式(100分)【文本处理】

    试题编号: 201912-3 试题名称: 化学方程式 时间限制: 1.0s 内存限制: 512.0MB 问题链接:CCF201912-3 化学方程式 问题简述:(略) 问题分析:文本处理问题,按字符串 ...

  4. CCF CSP 2019-9-1 小明种苹果 C语言100分

    CCF CSP 2019-9-1 小明种苹果 C语言100分 小明种苹果 完成时间11-18 16:54 代码长度510B C 正确 100分 耗时93ms 空间使用6.167MB 这道题简单,仅附上 ...

  5. ## CSP 201809-2 买菜(C语言)(100分)

    试题编号: 201809-2 试题名称: 买菜 时间限制: 1.0s 内存限制: 256.0MB 问题描述 小H和小W来到了一条街上,两人分开买菜,他们买菜的过程可以描述为,去店里买一些菜然后去旁边的 ...

  6. CCF202109-2 非零段划分(100分)【序列处理】

    试题编号: 202109-2 试题名称: 非零段划分 时间限制: 1.0s 内存限制: 512.0MB 问题描述: 题目描述 A1,A2,-,An是一个由 n 个自然数(非负整数)组成的数组.我们称其 ...

  7. CCF202009-4 星际旅行(100分题解链接)

    试题编号: 202009-4 试题名称: 星际旅行 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 在一个n维欧几里得空间的宇宙中,小 A 打算完成一次星际旅行. 利用n维直角 ...

  8. CCF202012-4 食材运输(100分题解链接)

    试题编号: 202012-4 试题名称: 食材运输 时间限制: 1.0s 内存限制: 512.0MB 问题描述: 题目背景 在T市有很多个酒店,这些酒店对于不同种类的食材有不同的需求情况,莱莱公司负责 ...

  9. CCF202006-4 1246【矩阵快速幂】(100分题解链接)

    试题编号: 202006-4 试题名称: 1246 时间限制: 1.0s 内存限制: 512.0MB 问题链接:CCF202006-4 1246 问题简述:(略) 问题分析:第一个参考链接是100分 ...

最新文章

  1. vue+antdesign导航菜单动态加载
  2. 网络经典命令行【网络高手必备】
  3. Redis命令:SETNX key value(SET if Not eXists)
  4. 其实昨天去加班也没有干什么事情,就只有3个人
  5. AtCoder Grand Contest #026 D - Histogram Coloring
  6. 京东连续亏损十几年,为什么却发展越来越大?
  7. 深入理解line-height与vertical-align——前端布局常用属性
  8. 关于Scalability的一些思考与疑问
  9. java--基本数据类型的转换(强制转换)
  10. kali linux 无线攻击——aircrack-ng
  11. react中可使用的video插件
  12. 用python计算数据的方差_用python求数据表中数据的均值与方差
  13. Python实现基于卷积神经网络的LSB算法进行信息隐藏隐写分析
  14. 易烊千玺成为罗莱家纺品牌代言人
  15. Appro DM36x IPNC 4.0 开发环境配置
  16. linux调节伽马值软件,LightBulb(屏幕伽马值自动调整)
  17. 油烟机烟雾报警_基于STC89C51单片机
  18. 计算机处理器i5和i7,i5与i7有什么区别 酷睿i5和i7区别汇总【详细介绍】
  19. NFT 金融化:存在哪些机会?面临哪些挑战?
  20. STM32f103微妙延迟函数

热门文章

  1. linux 卸载kde桌面,Ubuntu下完全卸载KDE的方法
  2. 关于如何优雅的做好代码注释
  3. 国内 Github 下载加速
  4. 强烈推荐的音视频资料(文中有福利哈!)
  5. 侠盗猎车手5 MOD版/Grand Theft Auto V/gta5
  6. vue2简单实现一个传送门的效果
  7. 你画我猜微信小程序源码
  8. 黑苹果安装四叶草配置宏碁ec 471g
  9. 解决网页重定向带来的无法返回的问题
  10. 在 Mac 上玩网游的简单方式