Python-CCF:20191203 化学方程式

文章目录

    • 题目
    • 思路
    • 代码

题目

Python-CCF:20191203 化学方程式_第1张图片
Python-CCF:20191203 化学方程式_第2张图片

测试用例来源
被某汤姓老师无情霸占写代码时间的我一拖再拖终于更了这道题
画上了一个圆满的省略号

思路

0分思路
其实我一开始写的版本思路贼他妈清奇
用Python做过那道二十四点的同学应该都知道
Python有个非常牛x的函数eval可以对str型的数学表达式求值
于是看到这种判断表达式是否相等的题我的心情异常激动
那怎么把化学方程式转化成数学表达式呢
相对原子质量还记得吗
可以把每个化学元素替换成唯一的一个数字
然后凑一下表达式的形式
最后用eval

当然要注意不能真的用相对原子质量
假如H的质量是1,He的质量是2
那么H2=He的结果就是Y了
显然有问题
聪明如我
立马上某度搜了质数表和元素周期表
自己在代码里写了个dict建立了元素到质数的映射
测试用例都过了没问题
但是天杀的CCF评测肯定用了不是正常化学元素的字母比如Aa Bb啥的
所以光荣的0分了

50分思路
然后乖乖地改为统计两边元素和了

4Au+8NaCN+2H2O+O2=4Na(Au(CN)2)+4NaOH

以此方程式为例
先用split('=')分为左右两部分
然后用split('+')把各个化学式分开
以最繁的化学式4Na(Au(CN)2)为例
用一个list记录其中的所有元素(可重复)

忽然想到用dict可能更好
比如 {‘H’: 2, ‘Au’: 5}

首先提取它的系数4
然后判断第一个是何种元素,或者是左括号
如果是元素则提取它的下标,向list中添加这个元素x次,x即为它的下标
如果是括号则递归调用自己,将括号内的字符串(子化学式)作为参数再传给此方法
最后返回化学式系数 * 元素list即此化学式的所有元素

思路没问题
然而递归超时了
比如测试用例

(((((((((((((A)13)12)11)10)9)8)7)6)5)4)3)2)1=1(2(3(4(5(6(7(8(9(10(11(12(13(A)))))))))))))

直接给我电脑跑死机了
最后只拿了50分

代码

def get_match_paren_index(left_paren_index, my_str):
    # 传入当前左括号的索引,和整个str,返回与其匹配的右括号的索引
    my_stack = []
    # 栈
    if my_str[left_paren_index] != '(':
        return None
    else:
        for i in range(left_paren_index, len(my_str), 1):
            if my_str[i] == '(':
                # 左括号则将其索引压栈
                my_stack.append(i)
            elif my_str[i] == ')':
                # 右括号则弹栈
                my_stack.pop()
            if not my_stack:
                # 如果弹栈后栈空则返回右括号的索引
                return i


def find_subscription(chemic_formula, element_start, element_type):
    # 输入不含系数的化学式,元素(双, 单, 左括号)的起始位置,元素的类型(doub, sing, lepa)
    # 返回    其下标, 元素的结束位置, 下标的结束位置

    step = 0
    # 跳过几个字母开始提取下标

    if element_type == "doub":
        step = 2
    elif element_type == "sing":
        step = 1
    elif element_type == "lepa":
        step = get_match_paren_index(element_start, chemic_formula) - element_start + 1
        # 找到与之匹配的右括号的索引,跳过step个长度开始提取下标
    subscription = '1'
    # 下标
    subscription_end = element_start + step
    # 默认下标为1时的结束位置(不含)

    if chemic_formula[element_start + step].isdigit():
        # 如果有数字则提取下标,否则下标为1
        for j in range(element_start + step, len(chemic_formula), 1):
            if not chemic_formula[j].isdigit():
                # 找到其后的第一个非数字
                subscription = chemic_formula[element_start + step: j]
                # 提取下标
                subscription_end = j
                # 改变下标结束的位置

                break
                # 找到符合条件的j则break
    return subscription, step, subscription_end


def rtn_result(chemic_formula):
    # 输入化学式如25Ag35NO3NO2(34Ba(OH)2)5TiO4
    print('* begin in: ', chemic_formula)
    all_elements = []
    # 存放所有元素到列表all_elements
    formula_coefficient = 1
    # 化学式系数
    chemic_formula += '.'
    # 防止下标越界

    if chemic_formula[0].isdigit():
        # 如果字符串首是数字则提取系数,否则系数为1
        for i in range(len(chemic_formula)):
            if not chemic_formula[i].isdigit():
                formula_coefficient = chemic_formula[0: i]
                # 提取化学式的系数

                chemic_formula = chemic_formula[i: -1] + chemic_formula[-1]
                # 将化学式去掉系数
                break
    i = 0
    while True:
        element = []
        element_type = ""
        if chemic_formula[i].isupper() and chemic_formula[i+1].islower():
            # 如果是双字母元素
            element.append(chemic_formula[i: i+2])
            element_type = "doub"
        elif chemic_formula[i].isupper() and not chemic_formula[i+1].islower():
            # 单字母元素
            element.append(chemic_formula[i: i+1])
            element_type = "sing"
        elif chemic_formula[i] == '(':
            # 左括号
            element = "paren"
            element_type = "lepa"
        subscription, step, subscription_end = find_subscription(chemic_formula, i, element_type)
        # 其下标, 元素的结束位置, 下标的结束位置

        if element == "paren":
            all_elements += rtn_result(chemic_formula[i+1: i+step-1]) * int(subscription)
            # 如果是括号则递归调用自己,最后返回括号内的所有元素再乘以下标

        else:
            all_elements += element * int(subscription)
        i = subscription_end
        # 直接跳到下一个元素的开始位置

        if (chemic_formula[i]) == '.':
            # 如果跳到最后的位置则结束
            print('* break from: ', chemic_formula)
            break
    return all_elements * int(formula_coefficient)


if __name__ == "__main__":
    expression_num = int(input())
    for en in range(expression_num):
        expression = input()
        former_expression, later_expression = expression.split('=')
        all_former_fomula = former_expression.split('+')
        all_later_formula = later_expression.split('+')
        sum_former = []
        sum_later = []
        for ff in all_former_fomula:
            sum_former += rtn_result(ff)
        sum_former.sort(key=None, reverse=False)
        for lf in all_later_formula:
            sum_later += rtn_result(lf)
        sum_later.sort(key=None, reverse=False)
        if sum_former == sum_later:
            print('Y')
        else:
            print('N')

测试用例

11
H2+O2=H2O
Cu+As=Cs+Au
H2+Cl2=2NaCl
2H2+O2=2H2O
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

NNNYYYYYYYY
13
AAAAAAA=AAAAAAA
AaAaAaAa=AaAaAaAa
ABCDEFG=GFEDCBA
AaABbBCcCDdDEeEFfFGgG=GgGFfFEeEDdDCcCBbBAaA
2AAAAAA=3AAAA
2AaAAaAAaAAaAAaAAaA=3AaAAaAAaAAaA
(((((((((((((A)))))))))))))=(((((((((((((A)))))))))))))
(((((((((((((Aa)))))))))))))=(((((((((((((Aa)))))))))))))
(((((((((((((AaA)))))))))))))=(((((((((((((AAa)))))))))))))
(((((((((((((A)13)12)11)10)9)8)7)6)5)4)3)2)1=1(2(3(4(5(6(7(8(9(10(11(12(13(A)))))))))))))
(((((((((((((A)1)12)3)10)5)8)7)6)9)4)11)2)13=1(12(3(10(5(8(7(6(9(4(11(2(13(A)))))))))))))
(A)(A)(A)(A)(A)(A)(A)=(A)(A)(A)(A)(A)(A)(A)
3A(A(A(A(A(((A))B2)2AB)2A)2A)2A)2A=3AA+6AA+12AA+24AA+48AAB+96ABB

YYYYYYYYYYYYY

你可能感兴趣的:(Python-CCF)