CCF认证 201912-3 《化学方程式》

配平的意思为 方程式两边的化学元素的种类和对应的数量相同
在这里插入图片描述

解题核心

1、使用 int[] 数组映射所有的原子,原子的数量最多109,int能容得下,最终比较等号两边的数组即可。

int intAtom(char a) //大写字母
{
    return a - 'A';
}
int intAtom(char a,char b) //大写字母+小写字母
{
    return (a - 'A' + 1) * 26 + b - 'a';
}

2、递归调用计算原子数量。

例如: 对于项 10A3(B2(C2D4)2E2)2,我举的这个项包含所有情况
计算过程,这个过程是递归

ans     = 10*ans1                 ans1 = A3(B2(C2D4)2E2)2
ans1   =  A*3 + ans2              ans2 = (B2(C2D4)2E2)2
ans2   =  ans3*2				  ans3 = B2(C2D4)2E2
ans3   =  B*2 + ans4              ans4 = (C2D4)2E2
ans4   =  ans5 * 2 +ans7          ans5 = C2D4             ans7 = E2
ans5   =  C*2 + ans6              ans6 = D4
ans6   =  D*4
ans7   =  E*2

如果看懂了上诉解题核心,问题已经解决,以下就是展现代码能力的了,即如何将算法用程序来实现

友情提示:若想取得更好的效果,提高自己编码能力,请先不要参照下列代码,自己根据上诉算法思路,完成代码编写,这样更能提高自己。


解题过程

1、字符串分割 split

例如
H2+O2=H2O 分割为 H2 O2 H2O
CH4+2O2=CO2+2H2O 分割为CH4 2O2 CO2 2H2O
分割符+、=
本题目中只有一个=,可能有多个+,并且=将化学方程式分成两部分。
写出一个方法,完成字符串分割

void split(string str,vector<string> &ans,char spacer)
//三个参数分别为 原字符串 分割后字符串集合,分割符

vector<string> left //左侧化学式
vector<string> right //右侧化学式

2、前导系数和化学式分离 NumPlusExpr

从左往右,找到第一个不是数字的位置,使用substr进行分离,然后前面的是数字字符串,后面是化学式;
对前面的数字字符串使用stoi方法转化为int

3、处理单个化学式 singleExpr

使用递归调用,具体看代码;

递归终止条件

到达化学式 边界 或者 碰到了右括号
统计化学式的时候,我们使用 <元素,数量>,可以得知使用pair<int,int>再好不过。

核心方法

可以使用题目例子,来自行模拟方法处理过程,如化学式A3(B2(C2D4)2E2)2
ptr为模拟指针位置,end为expr边界,传入的参数为expr长度。

vector<pair<int,int> > singleExpr(string expr,int &ptr,int end)
{
    vector<pair<int,int> > ans; //返回值

    if(expr[ptr]=='(')
    {
        ptr++; //指针后移一位
        ans = singleExpr(expr,ptr,end);

        if(expr[ptr]==')')
            //碰到右括号,处理右括号后面的数字
        {
            ptr++;

            int num = 1;
            string str="";
            while(ptr < end && isNum(expr[ptr]))
            {
                str += expr[ptr];
                ptr++;
            }
            if(str != "")
                num = stoi(str);

            for(int i=0;i<ans.size();i++)
                ans[i].second = ans[i].second*num;
        }
    }
    else if(isUpper( expr[ptr] ))
    {
        int atom;
        if( ptr+1 < end && isLower(expr[ptr+1]) )
        {
            atom = intAtom(expr[ptr],expr[ptr+1]);
            ptr += 2;
        }
        else
        {
            atom = intAtom(expr[ptr]);
            ptr++;
        }
        int num = 1;
        string str="";
        while(ptr < end && isNum(expr[ptr]))
        {
            str += expr[ptr];
            ptr++;
        }
        if(str != "")
            num = stoi(str);
        // 原子 数量 : atom num
        pair<int,int> p;
        p.first = atom;
        p.second = num;
        ans.push_back(p);
    }

    if(ptr<end && expr[ptr]!=')')
    //递归暂停的条件是到达 边界 或者 碰到了右括号
    {
        vector<pair<int,int> > t2;
        t2 = singleExpr(expr,ptr,end);
        ans.insert(ans.end(),t2.begin(),t2.end());
    }
    return ans;
}

以下是AC代码

在方法isEqual中,中间注释的部分是解题技巧,根据测试点的条件限制,花很少的时间(在考试中自己权衡)尝试先拿一些分,有兴趣可以看看。

#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 800;
int n;
/* 原子换算成int型的公式是
    大写字母 char-'A'
    大写字母+小写字母 (pre-'A'+1)*26 + char-'a'
*/
int resLeft[maxn];
int resRight[maxn];
bool isNum(char ch)
{
    return ch >= '0' && ch <= '9';
}
bool isUpper(char ch)
{
    return ch >= 'A' && ch <= 'Z';
}
bool isLower(char ch)
{
    return ch >= 'a' && ch <= 'z';
}
int intAtom(char a)
{
    return a - 'A';
}
int intAtom(char a,char b)
{
    return (a - 'A' + 1) * 26 + b - 'a';
}

vector<string> split(string str,char spacer)
{
    vector<string> v;
    int pos1,pos2;
    pos1=0;
    pos2=str.find(spacer);
    while( pos2 != string::npos )
    {
        v.push_back( str.substr(pos1,pos2-pos1) );
        pos1 = pos2 + 1;
        pos2 = str.find(spacer,pos1);    // 从str的pos1位置开始搜寻spacer
    }
    if(pos1 != str.length()) //分割最后一个部分
        v.push_back(str.substr(pos1));

    return v;
}

vector<pair<int,int> > singleExpr(string expr,int &ptr,int end)
{
    vector<pair<int,int> > ans; //返回值

    if(expr[ptr]=='(')
    {
        ptr++; //指针后移一位
        ans = singleExpr(expr,ptr,end);

        if(expr[ptr]==')')
            //碰到右括号,处理右括号后面的数字
        {
            ptr++;

            int num = 1;
            string str="";
            while(ptr < end && isNum(expr[ptr]))
            {
                str += expr[ptr];
                ptr++;
            }
            if(str != "")
                num = stoi(str);

            for(int i=0;i<ans.size();i++)
                ans[i].second = ans[i].second*num;
        }
    }
    else if(isUpper( expr[ptr] ))
    {
        int atom;
        if( ptr+1 < end && isLower(expr[ptr+1]) )
        {
            atom = intAtom(expr[ptr],expr[ptr+1]);
            ptr += 2;
        }
        else
        {
            atom = intAtom(expr[ptr]);
            ptr++;
        }
        int num = 1;
        string str="";
        while(ptr < end && isNum(expr[ptr]))
        {
            str += expr[ptr];
            ptr++;
        }
        if(str != "")
            num = stoi(str);
        // 原子 数量 : atom num
        pair<int,int> p;
        p.first = atom;
        p.second = num;
        ans.push_back(p);
    }

    if(ptr<end && expr[ptr]!=')')
    //递归暂停的条件是到达 边界 或者 碰到了右括号
    {
        vector<pair<int,int> > t2;
        t2 = singleExpr(expr,ptr,end);
        ans.insert(ans.end(),t2.begin(),t2.end());
    }
    return ans;
}
void numPlusExpr(string expr,int res[])
{
    /*
     * 分离前导系数和化学式
     * */
    int pos=0; //化学式的开头位置
    for(int i=0;i<expr.size();i++)
        if(expr[i]>='0'&&expr[i]<='9')
            continue;
        else{
            pos=i;
            break;
        }
    int num=0;
    if(pos==0)
        num = 1;
    else
        num = stoi(expr.substr(0,pos));

    string subStr = expr.substr(pos); //从pos一直到结尾

    vector<pair<int,int> > v;
    int ptr = 0;
    v = singleExpr(subStr,ptr,subStr.length());
    for(int i=0;i<v.size();i++)
    {
        int atom = v[i].first;
        int number = num * v[i].second;
        res[atom] += number;
    }

}
void statAtom(vector<string> vec,int res[])
// 统计原子的数量,所有部分原子数相加之和,第二个参数为统计数组
{
    for(int i=0;i<vec.size();i++)
        numPlusExpr(vec[i],res);
}

bool isEqual(string expr)
{
    string l,r;
    vector<string> t;
    t = split(expr,'=');
    l = t[0];      //化学方程式左边
    r = t[1];      //化学方程式右边

// 测试点编号1、2 只有大写字母和等号,先编写代码得20分
// 排个序就好,如果测试点是个狼人,弄个10^9个大写字母,排序就不管用了。
//    string lt_1 = l,rt_1 = r;
//    sort(lt_1.begin(),lt_1.end());
//    sort(rt_1.begin(),rt_1.end());
//    if(lt_1 != rt_1)
//        return false;

/* 提交结果,答案错误,但竟然拿下了30分 哈哈 */

// 测试点编号3、4 加入小写字母和加号
// 拿个40分

//    vector<int> lt_2;
//    vector<int> rt_2;
// 原子可以为 大写字母 或者 大写字母小写字母
//    lt_2 = toAtom(l);
//    rt_2 = toAtom(r);
//
//    sort(lt_2.begin(),lt_2.end());
//    sort(rt_2.begin(),rt_2.end());
//
//    if(lt_2 != rt_2)
//        return false;

    vector<string> left,right;
    left = split(l,'+');
    right = split(r,'+');
    statAtom(left,resLeft);
    statAtom(right,resRight);
    for(int i=0;i<maxn;i++)
        if(resLeft[i] != resRight[i])
            return false;
    return true;
}

int main() {
    cin>>n;
    for(int i=0;i<n;i++)
    {
        fill(resLeft,resLeft+maxn,0);
        fill(resRight,resRight+maxn,0);
        string expr;
        cin>>expr;
        isEqual(expr)?printf("Y\n"):printf("N\n");
    }
    return 0;
}
/*
 * 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=Ba3(PO4)2+6H2O
 * 3Ba(OH)2+2H3PO4=6H2O+Ba3(PO4)2
 * 4Zn+10HNO3=4Zn(NO3)2+NH4NO3+3H2O
 * 4Au+8NaCN+2H2O+O2=4Na(Au(CN)2)+4NaOH
 * Cu+As=Cs+Au
 * */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值