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

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

回收站选址
这道题是第一次提交满分,之前是因为数值涉及太大,我不知道怎么存储以及判断会省时一些,就一直没做;这次做用到了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,以前的第三题真是简单啊!!!尖叫!以及,可以修改得到满分真的特别有成就感!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值