CCF 2019-3化学方程式

转载

/*首先要清楚系数出现位置的三种情况:
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分别记录左右两边的元素个数,再进行比较。*/

#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;

struct Elem { //元素
    string name; //名称
    int num; //个数
    Elem(string _name, int _num) : name(_name), num(_num) {}
};

int toNumber(string str, int &pos) { //从str的pos位置开始,得到一个数字
    int num = 0;
    while (isdigit(str[pos])) {
        num = num * 10 + str[pos] - '0';
        pos++;
    }
    return num;
}

void calc(string &str, map<string, int> &mp) {
    stringstream ss(str);
    string item;

    while (getline(ss, item, '+')) { //获取每一个化学式,如 32Ba((OH)2(CO3)2)3

        vector<Elem> arr; //存储化学式的分解序列, 如 Ba、(、(、O、H、)、(、C、O、)、)
        int factor = 1; //整个化学式的系数,默认为1
        int i = 0;

        if (isdigit(item[i])) factor = toNumber(item, i); //计算化学式系数

        while (i < item.size()) {

            if (isdigit(item[i])) { //处理数字
                int num = toNumber(item, i);
                //第一种情况 系数是括号右边的
                if (arr[arr.size() - 1].name == ")") { //序列最后一个元素是右括号
                    int j = arr.size() - 1;
                    arr[j].name = "*"; //将右括号标记为*,忽略它的存在
                    while (arr[--j].name != "(") {
                        arr[j].num *= num;
                    }
                    arr[j].name = "*"; //将左括号标记为*,忽略它的存在
                }
                    // //第二种情况 系数是元素右边的
                else arr[arr.size() - 1].num *= num; //序列最后一个元素是元素名称
            } else if (item[i] == '(') { //处理左括号
                arr.push_back(Elem("(", 0));  //括号加入到序列中
                i++;
            } else if (item[i] == ')') { //处理右括号
                arr.push_back(Elem(")", 0));  //括号加入到序列中
                if (!isdigit(item[i + 1])) item.insert(i + 1, "1"); //考虑到右括号右边可能不出现数字,补充底数1
                i++;
            } else if (isupper(item[i])) {  //处理大写字母
                //得到元素名称
                string name = "";
                name += item[i]; //大写字目
                i++;
                if (islower(item[i])) { //小写字母
                    name += item[i];
                    i++;
                }
                arr.push_back(Elem(name, 1)); //名称加入到序列中
            }
        }

        for (int i = 0; i != arr.size(); ++i) { //将“元素->个数”保存到map中
            if (arr[i].name == "*") continue; //忽略序列中括号的存在
            mp[arr[i].name] += arr[i].num * factor;
        }

    }

}

bool judge(map<string, int> &left, map<string, int> &right) { //判断两个map是否相同
    if (left.size() != right.size()) return false;
    for (map<string, int>::iterator it = left.begin(); it != left.end(); ++it) {
        if (right[it->first] != it->second) return false;
    }
    return true;
}

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        map<string, int> left, right;
        string str, lstr, rstr;
        cin >> str;
        stringstream ss(str);
        getline(ss, lstr, '='); //得到等号左边的字符串
        getline(ss, rstr); //得到等号右边的字符串

        calc(lstr, left); //计算左字符串
        calc(rstr, right);

        if (judge(left, right)) cout << "Y" << endl;
        else cout << "N" << endl;
    }
    return 0;
}

/*
1
2H2+02=H20

 CaCl2+2AgN03=Ca(N)O32+2AgCl



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

 */

你可能感兴趣的:(CCF计算机软件能力认证)