201912 CSP认证 | 回收站选址 化学方程式

文章讲述了作者在C++编程中解决回收站选址问题的过程,使用了STL集合和优化算法处理字符串,尤其是嵌套括号的计数,以及从80分到满分的思路转变。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

回收站选址
这道题是第一次提交满分,之前是因为数值涉及太大,我不知道怎么存储以及判断会省时一些,就一直没做;这次做用到了STL,也算是做了这么多第三题回看第二题的一些简便吧
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
set<PII> se;
int res[10];
int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n; cin >> n;

    for(int i = 0;i < n;i ++){
        int x, y; cin >> x >> y;
        se.insert({x, y});
    }

    int idx[] = {-1, 1, 0, 0, -1, -1, 1, 1};
    int idy[] = {0, 0, -1, 1, -1, 1, -1, 1};
    for(auto point : se){
        int x = point.first, y = point.second;
        bool flag = true;
        for(int i = 0;i < 4 && flag;i ++){   //看是否符合要求
            int dx = x + idx[i];
            int dy = y + idy[i];
            if(!se.count({dx, dy})) { flag = false; break; }
        }

        if(flag){   //符合要求的基础上,统计得分
            int cnt = 0;
            for(int i = 4;i < 8;i ++){
                int dx = x + idx[i];
                int dy = y + idy[i];
                if(se.count({dx, dy})) cnt ++;
            }
            res[cnt] ++;
        }
    }
    for(int i = 0;i <= 4;i ++){
        cout << res[i] << endl;
    }
    return 0;
}


化学方程式
第一把官网提交是80分,acwing通过的答案是 9 / 11;先贴出80分的初始代码:

#include<bits/stdc++.h>
#define key first
#define value second
using namespace std;
typedef pair<string, int> PSI;
int to_int(string str)
{
    stringstream ssin(str);
    int x;
    ssin >> x;
    return x;
}
bool upper(char ch)
{
    if(ch >= 'A' && ch <= 'Z') return true;
    return false;
}
bool lower(char ch)
{
    if(ch >= 'a' && ch <= 'z') return true;
    return false;
}
bool number(char ch)
{
    if(ch >= '0' && ch <= '9') return true;
    return false;
}

unordered_map<string, int> cal(string str)
{
    unordered_map<string, int> res;
    vector<string> expr;

    for(int i = 0, j = 0; i < str.size(); i = j + 1){  //将str用'+'分割开来
        j = str.find('+', i);  //从下标i开始找+号
        if(j == -1) j = str.size();
        expr.push_back(str.substr(i, j - i));
    }

    for(auto item : expr){    //对于每一个物质
        unordered_map<string, int> cnt;   //建立一个小的map为了处理好下标乘系数
        int coef = 1, pos = 0;
        if(number(item[0])){   //提取系数
            while(number(item[pos])) pos ++;
            coef = to_int(item.substr(0, pos));
        }
        while(pos < item.size()){
            if(upper(item[pos])){   //元素
                string ele = "";

                if(pos < item.size() - 1 && lower(item[pos + 1])) {   //大写+小写的组合
                    ele = item[pos] + item[pos + 1];
                    pos += 2;
                }
                else { ele = item[pos]; pos ++; }
                if(pos < item.size() && number(item[pos])){   //如果元素后面跟了数字,则开始找下标
                    int l = pos;
                    while(pos < item.size() && number(item[pos])) pos ++;
                    int x = to_int(item.substr(l, pos));
                    //cout << "ele"<<ele<<"的下标是"<<x<<endl;
                    cnt[ele] += x;
                }
                else cnt[ele] ++;  //出现一次
            }

            else if(item[pos] == '('){  //开始处理括号
                stack<PSI> s;
                s.push({"(", 0}); pos ++;
                unordered_map<string, int> temp;  //是为了处理下标

                while(!s.empty() && pos < item.size()){
                    //跟遇到元素的处理方式其实相同
                    if(upper(item[pos])){   //元素
                        string ele;
                        if(pos < item.size() - 1 && lower(item[pos + 1])) {   //大写+小写的组合
                            ele = item[pos] + item[pos + 1];
                            pos += 2;
                        }
                        else { ele = item[pos]; pos ++; }
                        int sub = 1;
                        if(pos < item.size() && number(item[pos])){   //元素后面跟了数字
                            int l = pos;
                            while(pos < item.size() && number(item[pos])) pos ++;
                            sub = to_int(item.substr(l, pos));
                        }
                        s.push({ele, sub});   //压入元素
                    }
                    else if(item[pos] == '(')   { s.push({"(", 0}); pos ++; }  //只是入栈不做操作 | 此时不可能是item的末尾,不涉及越界
                    else if(item[pos] == ')'){
                        pos ++; int sub = 1;  //先提取括号外的下标
                        if(pos < item.size() && number(item[pos])){
                            int l = pos;
                            while(pos < item.size() && number(item[pos])) pos ++;
                            sub = to_int(item.substr(l, pos));   //下标更新
                        }

                        //开始弹出,和当前反括号匹配的括号内的元素,都要乘下标
                        auto x = s.top(); s.pop();
                        while(x.key != "("){
                            temp[x.key] += (x.value * sub);
                            x = s.top(); s.pop();
                        }
                    }
                }
                //将temp里的并到cnt里去
                for(auto x : temp){
                    cnt[x.key] += x.value;
                }
            }
        }

        //所有个数都要乘以系数
        for(auto x : cnt){
            res[x.key] = res[x.key] + x.value * coef;
        }
    }
    return res;
}

bool judge(string expr)
{
    unordered_map<string, int> eleft, eright;  //等式左右两侧各一个map
    int i = 0;
    while(expr[i] != '=') i ++;
    string ex1 = expr.substr(0, i);
    string ex2 = expr.substr(i + 1);
    eleft = cal(ex1);
    eright = cal(ex2);
    if(eleft.size() != eright.size()) return false;
    for(auto x : eleft){
        if(eright[x.key] != x.value) return false;
    }
    return true;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n; cin >> n;

    string line;
    while(n --){
        cin >> line;
        bool flag = judge(line);
        if(flag) cout << "Y\n";
        else cout << "N\n";
    }
    return 0;
}

做这么几次字符串的题目发现,字符串题目的思维难度其实不大,最大的难点就是很难将所有情况考虑全面,属于是有例子在,完全可以满分;但是会很容易忽略一些细节,导致搜索的过程中提前将答案也过滤掉了
所以这次修改也是基于ACWING的答案上;在比对时我发现了嵌套时出现了这样的问题:
在这里插入图片描述
我简化了左侧的表达式,使得问题更加清楚;K明显不止32个,我之前的做法遗漏了外层括号

在之前的做法中,每遇到一个回括号,我就开始弹出栈内的元素直到遇见(,也就是与之匹配的首部括号,然后根据下标更新括号内的元素数目。
这就是问题所在。栈顶元素按照逻辑是最内层的括号里的元素!它在所有括号的内部。而在上述做法中,这些元素被更新一次就被弹出了,而实际上这些元素应该被更新到最外层的括号截止,才算完成。所以在修改后的做法中,我设置了一个容器先暂存这些弹出的栈顶元素,如果栈非空的话,需要将这些元素弹回(在外层括号的下标还要做乘法)

最终修改得到满分答案,官网截图以及代码如下:
在这里插入图片描述

#include<bits/stdc++.h>
#define key first
#define value second
using namespace std;
typedef pair<string, int> PSI;
//将字串转化为整数
int to_int(string str)
{
    stringstream ssin(str);
    int x;
    ssin >> x;
    return x;
}
//判断是否为大写字母
bool upper(char ch)
{
    if(ch >= 'A' && ch <= 'Z') return true;
    return false;
}
//判断是否为小写字母
bool lower(char ch)
{
    if(ch >= 'a' && ch <= 'z') return true;
    return false;
}
//判断是否为数字
bool number(char ch)
{
    if(ch >= '0' && ch <= '9') return true;
    return false;
}
//计算一个fomular的所有元素及其个数
unordered_map<string, int> cal(string str)
{
    unordered_map<string, int> res;
    vector<string> expr;

    for(int i = 0, j = 0; i < str.size(); i = j + 1){  //将str用'+'分割开来
        j = str.find('+', i);  //从下标i开始找+号
        if(j == -1) j = str.size();
        expr.push_back(str.substr(i, j - i));
    }

    for(auto item : expr){    //对于每一个物质
        unordered_map<string, int> cnt;   //建立一个小的map为了处理好下标乘系数
        int coef = 1, pos = 0;
        if(number(item[0])){   //提取系数
            while(number(item[pos])) pos ++;
            coef = to_int(item.substr(0, pos));
        }
        while(pos < item.size()){   //分析物质
            if(upper(item[pos])){   //元素
                string ele = "";

                if(pos < item.size() - 1 && lower(item[pos + 1])) {   //大写+小写的组合
                    ele = item[pos] + item[pos + 1];
                    pos += 2;
                }
                else { ele = item[pos]; pos ++; }
                if(pos < item.size() && number(item[pos])){   //如果元素后面跟了数字,则开始找下标
                    int l = pos;
                    while(pos < item.size() && number(item[pos])) pos ++;
                    int x = to_int(item.substr(l, pos));
                    cnt[ele] += x;
                }
                else cnt[ele] ++;  //出现一次
            }

            else if(item[pos] == '('){  //开始处理括号
                stack<PSI> s;    //s中压入括号,或者括号内的元素及其出现次数
                s.push({"(", 0}); pos ++;
                vector<PSI> temp;   //栈顶弹出元素暂存区

                while(!s.empty() && pos < item.size()){
                    //跟遇到元素的处理方式其实相同
                    if(upper(item[pos])){   //出现元素
                        string ele;
                        if(pos < item.size() - 1 && lower(item[pos + 1])) {   //大写+小写的组合
                            ele = item[pos] + item[pos + 1];
                            pos += 2;
                        }
                        else { ele = item[pos]; pos ++; }
                        int sub = 1;
                        if(pos < item.size() && number(item[pos])){   //元素后面跟了数字
                            int l = pos;
                            while(pos < item.size() && number(item[pos])) pos ++;
                            sub = to_int(item.substr(l, pos));
                        }
                        s.push({ele, sub});   //压入元素及其个数
                    }
                    else if(item[pos] == '(')   { s.push({"(", 0}); pos ++; }  //只是入栈不做操作 | 此时不可能是item的末尾,不涉及越界
                    else if(item[pos] == ')'){
                        pos ++; int sub = 1;  //先提取括号外的下标
                        if(pos < item.size() && number(item[pos])){
                            int l = pos;
                            while(pos < item.size() && number(item[pos])) pos ++;
                            sub = to_int(item.substr(l, pos));   //下标更新
                        }

                        //开始弹出,和当前反括号匹配的括号内的元素,都要乘下标
                        auto x = s.top(); s.pop();
                        while(x.key != "("){
                            x.value *= sub;
                            temp.push_back(x);   //暂存到temp中去
                            x = s.top(); s.pop();
                        }
                        if(!s.empty()){   //还没到最大的括号(注意内层括号被包括在外层括号之中)
                            //暂存区中的元素需要弹回栈内
                            for(auto x : temp){
                                s.push(x);
                            }
                            temp.clear();
                        }
                    }
                }
                //将temp里的并到cnt里去
                for(auto x : temp){
                    cnt[x.key] += x.value;
                }
            }
        }

        //所有元素个数都要乘以物质个数(系数)
        for(auto x : cnt){
            res[x.key] = res[x.key] + x.value * coef;
        }
    }
    return res;
}

bool judge(string expr)
{
    unordered_map<string, int> eleft, eright;  //等式左右两侧各一个map
    int i = 0;
    while(expr[i] != '=') i ++;
    string ex1 = expr.substr(0, i);
    string ex2 = expr.substr(i + 1);
    eleft = cal(ex1);
    eright = cal(ex2);
    if(eleft.size() != eright.size()) return false;
    for(auto x : eleft){
        if(eright[x.key] != x.value) return false;
    }
    return true;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n; cin >> n;

    string line;
    while(n --){
        cin >> line;
        bool flag = judge(line);
        if(flag) cout << "Y\n";
        else cout << "N\n";
    }
    return 0;
}

OVER,以前的第三题真是简单啊!!!尖叫!以及,可以修改得到满分真的特别有成就感!!

### CSP 认证考试中的化学方程式配平 CSP(Certified Software Professional)认证考试中,化学方程式的题目通常涉及通过编程方法来解决化学方程式的配平问题。这类问题的核心在于利用线性代数的方法求解未知变量的最小整数倍数,使得反应物和生成物中原子的数量保持平衡。 #### 输入与输出格式 输入数据由多行组成,每一行描述一个需要被配平的化学方程式。具体来说,每行包含一个正整数 \( m \),表示方程式中有多少种物质参与反应,以及紧随其后的 \( m \) 个字符串,这些字符串代表了反应物和生成物的化学式[^1]。 输出则是一个经过配平的化学方程式,其中每个化学式的前缀系数满足原子守恒原则,并且所有系数均为非负整数[^2]。 #### 配平方程式的原理 化学方程式配平的本质是找到一组合适的系数,使反应物和生成物中各元素的原子数量完全一致。例如,在给定的例子中: \[ 2\text{H}_2 + \text{O}_2 = 2\text{H}_2\text{O} \] 左侧氢原子总数为 4,氧原子总数为 2;右侧同样有 4 个氢原子和 2 个氧原子,因此该方程式被认为是配平的[^3]。 为了实现这一目标,可以采用高斯消元法或其他数值计算技术,构建并求解相应的矩阵方程组。这种方法能够有效处理复杂的化学反应场景。 以下是基于 Python 的一种可能解决方案,用于自动完成化学方程式的配平操作: ```python from sympy import symbols, Eq, solve def parse_formula(formula): """解析单个化学式,返回字典形式的结果""" elements = {} i = 0 while i < len(formula): if formula[i].isupper(): elem_start = i i += 1 while i < len(formula) and formula[i].islower(): i += 1 element = formula[elem_start:i] count = 1 if i < len(formula) and formula[i].isdigit(): start_digit = i i += 1 while i < len(formula) and formula[i].isdigit(): i += 1 count = int(formula[start_digit:i]) if element not in elements: elements[element] = 0 elements[element] += count return elements def balance_equation(equation): reactants, products = equation.split('=') reactant_list = list(map(parse_formula, reactants.strip().split('+'))) product_list = list(map(parse_formula, products.strip().split('+'))) all_elements = set() for r in reactant_list: all_elements.update(r.keys()) for p in product_list: all_elements.update(p.keys()) variables = symbols(' '.join(f'x{i}' for i in range(len(reactant_list) + len(product_list)))) equations = [] for e in all_elements: lhs = sum(variables[i] * reactant_list[i].get(e, 0) for i in range(len(reactant_list))) rhs = sum(variables[len(reactant_list)+j] * product_list[j].get(e, 0) for j in range(len(product_list))) equations.append(Eq(lhs, rhs)) solution = solve(equations, variables) coefficients = [solution[var] for var in variables] lcm = abs(coefficients[0].as_numer_denom()[1]) for c in coefficients[1:]: lcm = lcm.lcm(c.as_numer_denom()[1]) normalized_coefficients = [int(lcm * c / c.as_numer_denom()[1]) for c in coefficients] min_index = normalized_coefficients.index(min(normalized_coefficients)) factor = normalized_coefficients[min_index] final_coeffs = [c // factor for c in normalized_coefficients] balanced_reactants = ' + '.join([f'{final_coeffs[i]} {list(equation.split("="))[0].strip().split("+")[i]}' for i in range(len(list(equation.split("="))[0].strip().split("+"))]) balanced_products = ' + '.join([f'{final_coeffs[i+len(reactant_list)]} {list(equation.split("="))[1].strip().split("+")[i]}' for i in range(len(list(equation.split("="))[1].strip().split("+"))]) return f"{balanced_reactants} = {balanced_products}" equation_input = input("请输入未配平的化学方程式 (e.g., H2 + O2 = H2O): ") print(balance_equation(equation_input)) ``` 此代码片段实现了对任意简单化学方程式的自动化配平功能[^4]。 #### 结论 综上所述,CSP 考试中的化学方程式配平问题是计算机科学与化学交叉领域的重要应用之一。它不仅考察考生对于基础算法的理解能力,还测试他们跨学科解决问题的能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值