CCF-CSP认证 201912-3 化学方程式

CCF-CSP认证 201912-3 化学方程式

题意描述

给出n个化学方程式 让我们判断化学方程式是否配平

输入输出格式

输入第一行包含一个整数n表示方程式的数量 接下来是n行化学方程式

数据规模

n在[1,100] 之间

算法设计

个人认为 这应该是CSP第三题中最好读懂题意的题目了 就是给几个化学方程式 让我们去判断方程式是否配平 直接上字符串处理 但是说来容易 仔细想一想 我们需要考虑的细节还是不少的 我列出了以下需要解决的问题 以及相应的解决方案

  1. 如何去判断方程式是平的?
    我们建立一张图 map<string,gg> gg 即是 long long 用来统计元素出现的次数 方程式等号左边出现的元素使得该元素在图中的计数++ 而右边的元素使得该计数-- 最后遍历 map 只要有元素的个数不为0 即为方程式不平 反之 方程式就是配平的
  2. 以什么为处理的单位?
    在这里我们以一个表达式作为处理的单位 将字符串用+号分割 分割出来的即是表达式 这里的表达式可以是H2 H20 Cu(OH)2 我们单独编写函数来处理这样的表达式
  3. 数字的处理?
    在处理函数中 开始必须计算整个表达式的总系数 而对于每个元素 它的个数便是总系数乘以元素后面的数字 我们单独编写函数来处理数字 而且 参数是引用类型 方便我们处理下一个表达式
  4. 括号的处理?
    这里便体现了我们以表达式作为处理对象的好处了 比如Cu(OH)2 这里括号包裹的显然也是一个表达式 只不过这个表达式的初始系数等于原来的系数 乘以 括号后面的系数 那我们显然可以递归地调用处理表达式的函数

模拟题比较繁琐 希望读者可以耐心地在这些问题的引导下 自己独立编码实现 调试 解决bug 我在代码里添加了一定的注释 希望对实现有所帮助

c++11代码

#include <bits/stdc++.h>
using namespace std;
using gg = long long;
unordered_map<string, gg> mp;//  全局图
gg computenum(const string& s,gg& start){  // start是数字的开头 显然 计算过后的start 指向的不再是数字
    gg res = 0;   //  初值为0
    while(start<s.size() and isdigit(s[start])){
        res = res * 10 + (s[start] - '0');
        start++;
    }
    return (res == 0 ? 1 : res);   //  总系数不能是0啊!
}

void deal(const string& s,gg e){  // 开始考虑单个字符的计算  e 是这个表达式的总系数  快慢指针处理字符串
    gg i = 0, j;  // i是处理字符的起点
    e *= computenum(s, i);   // 计算总系数
    while(i < s.size()){
        if(isupper(s[i])){  // 确定是一个元素了
            if(i+1 < s.size() and islower(s[i+1])){     // 双原子元素
                gg k = i;
                i += 2;  // 指向元素后面的数字
                mp[s.substr(k, 2)] += e * computenum(s,i);   // 此时 i以及指向了下一个判断位了
            }else{   // 单原子元素
                gg k = i;
                i += 1;
                mp[s.substr(k, 1)] += e * computenum(s, i);
            }
        }else{    // 不是元素 开始考虑括号    其实 括号的内部 就是一个结果相同的表达式
            gg left = i, right = i + 1, num = 1; // 开始寻找括号的右边界   处理区间即为[left+1,right-1]
            while(num!=0 and right < s.size()){
                if(s[right] == '(')
                    num++;
                else if(s[right] == ')')
                    num--;
                right++;
            }
            deal(s.substr(left + 1, right - left - 2), e * computenum(s, right));
            i = right;
        }
    }
}

void compute(const string& s,gg exp){   // 把整个表达式进行了分割
    gg i, j;
    for (i = 0; i < s.size();){
        j = s.find('+', i);
        if(j == string::npos){
            deal(s.substr(i),exp);
            i = s.size();
        }else{
            deal(s.substr(i, j - i),exp); // 第二参数是子串的长度
            i = j + 1;
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    gg ni;
    cin >> ni;
    string s;
    while (ni--){
        cin >> s; //   要处理的化学方程式
        auto j = s.find('=');
        compute(s.substr(0, j), 1); // 计算左边
        compute(s.substr(j + 1),-1);   //  计算右边  右边要减去 参数是 -1 
        for(auto i : mp){
           if(i.second !=0){
               cout << "N\n";
               goto loop;
           }
        }
        cout << "Y\n";
        loop:;
        mp.clear();   // 图一定要清空!
   }
   return 0;
}

题目链接

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值