CCF CSP认证考试201912题解(1,2,3,4)

【缘起】

从acm退役十个月了,目前正在准备研究生复试,其中机试部分与CCF题目相仿,所以练习一下CCF SCP题目。记一下题解,加深印象。

【题目来源】

http://www.cspro.org/     

进入后点击报名入口,注册or登录,然后右上角有个模拟考试,点击去会看到95道往届CCF CSP认证真题。以下题解按考试日期编号。由于官网题面给的是图片,这里就不放题面了。给出每个题的链接。

文章较长,推荐您按Ctrl+F输入题目名字以快速找到题目。

2019年12月CCF CSP认证

201912-1 报数 

【分析】签到题,题目数据很小,直接for循环跑一遍,每次迭代判断一下当前数字i是否含有7或是7的倍数即可。

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int has7(int x)
{
    while(x){
        if(x%10==7)
            return 1;
        x/=10;
    }
    return 0;
}
int main()
{
    int n, ans[5]={0};
    cin>>n;
    for(int i=1,tot=1;tot<=n;i++)
    {
        if(i%7==0 || has7(i))
            ans[i%4]++;
        else tot++;
    }
    for(int i=1;i<=4;i++)
        cout<<ans[i%4]<<endl;
}

201912-2 回收站选址 

【分析】

由于坐标过大,可利用C++之STL库的map(STL库网上有很多详解,不赘述)。map用法和python里的字典极为相似。该题map的key值是坐标(建议用struct{ int x,y;},我用的是STL库的pair,本质就是个结构体)。

输入坐标是,用map标记每个坐标为1,然后遍历坐标,判断八个方位的坐标情况(被标记为1的说明有坐标,否则无)。依题,先判断该点能否作为垃圾桶,然后统计斜四方的坐标存在个数。

延伸:其实本题用STL库set就足够了,没必要用map,因为map的值没用到,只需要判断是否存在。

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int dir[9][2]={-1,0, 1,0, 0,-1, 0,1, -1,1, -1,-1, 1,-1, 1,1};
int main()
{
    int n,x[1010],y[1010],ans[6]={0};
    map<pair<int,int>,int> mmp;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        scanf("%d%d",&x[i],&y[i]);
        mmp[pair<int,int>(x[i],y[i])]=1;
    }
    for(int i=0;i<n;i++)
    {
        int isTrash=true;
        for(int j=0;j<4;j++){
            if(mmp.find(pair<int,int>(x[i]+dir[j][0],y[i]+dir[j][1]))==mmp.end()){
                isTrash=false;
                break;
            }
        }
        if(isTrash){
            int tot=0;
            for(int j=4;j<8;j++){
                if(mmp.find(pair<int,int>(x[i]+dir[j][0],y[i]+dir[j][1]))!=mmp.end())
                    tot++;
            }
            ans[tot]++;
        }
    }
    for(int i=0;i<5;i++)
        cout<<ans[i]<<endl;
}

201912-3 化学方程式 

【分析】

这题挺恶心的。。。借助map和string可以方便些。 由括号嵌套应该想到递归(或理解为树)的思想,每多一层,就要递归一层,括号内的内容可以当作全新的一个分子。 用map记录每种原子的数量,最终比较map所存的原子数量是否相等。

延伸:也可以不用map,每读到一种原子,就为其赋予一个数字编号id,如此就可以用数组代替上文的map。这样运行时间会短很多。

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int n;
char str[1010];
map<string,int> L,R;
int myRight[1010]; //记下当前左括号的右括号位置

int read_num(char *p,int &num)//从字符串读取一个数字num,返回长度
{
    num=0;
    int len=0;
    while(*p && isdigit(*p)){
        num=num*10+*p-'0';
        p++;
        len++;
    }
    return len;
}

string get_atom(int &i)  //给出str的下标,读取一个原子. 注意题目中描述的元素写法
{
    string ret="";
    if(isupper(str[i]))
    {
        ret+=str[i++];
        if( islower( str[i] ) ) //大写+小写,如Ca
            ret+=str[i++];
    }
    return ret;
}

//统计一个分子式各原子数量,ended是右端点(包含)
void read_element(int start, int ended, map<string,int> &Map, int multi)
{
    int index,len;
    for(int i=start;i<=ended;)
    {
        if(str[i]=='(') //遇到了括号:((CaCl)4Me(NaCl)3)6
        {
            len = read_num(&str[ myRight[i]+1 ], index);
            if(len == 0) index=1; //没有下标, 默认1
            read_element(i+1, myRight[i]-1, Map, multi*index); //递归
            i=myRight[i]+1+len;
            continue;
        }

        string atom = get_atom(i); //读取一个原子, 同时使i自增
        len = read_num(&str[i], index);  //读后缀下标
        if(len == 0) index=1; //没有下标, 默认1
        if(Map.find(atom)==Map.end())
            Map[atom]=0;
        Map[atom]+=multi*index; //统计该原子个数
        i+=len;
    }
}

bool compare(map<string,int>&L,map<string,int>&R) //比较两个map内容是否一致
{
    if(L.size()!=R.size())return false; //不一致
    for(map<string,int>::iterator it=L.begin(); it!=L.end(); it++)
    {
        if(R.find(it->first)==R.end())return false;
        if(R[it->first]!=it->second)return false;
    }
    return true;
}


void solve()
{
    scanf("%s",str);
    int sta[1010], top=0; //栈
    for(int i=0, deep=0; str[i]; i++)
    {
        if(str[i]=='(')sta[top++] = i;  //左括号位置入栈暂存
        if(str[i]==')')myRight[sta[--top]] = i; //出栈,让左括号记住其右括号位置
    }

    bool isRight=false; //处于等式右边了吗,初始在左边
    for(int start=0,i=0;; i++)
    {
        if(str[i]=='+' || str[i]=='=' || str[i]=='\0') //标志着一个分子式的结束
        {
            int xishu, len;
            len=read_num(&str[start],xishu);
            if(len==0)
                xishu=1;  //系数默认为1

            if(isRight) read_element(start+len, i-1, R, xishu); //读取一个分子式,并统计原子数
            else read_element(start+len, i-1, L, xishu);
            start=i+1; //下一个分子式起点
        }
        if(str[i]=='=')
            isRight=true;
        if(str[i]=='\0')break;
    }

    if(compare(L,R))puts("Y");
    else puts("N");
}
int main()
{
    scanf("%d",&n);
    while(n--){
        L.clear();
        R.clear();
        solve();
    }
}

201912-4 区块链 

【分析】(内心:这道题读着读着就吐了)下面的代码只能得80分,参考价值应该不大了。。。思路就是按照题意模拟过程,跑时间轴(一开始就担心超时,果不其然),这是比较笨的方法,因为题目没说时间上限。

言归正传,题目保证输入的时间递增,故遍历时间,每个时间点,先发送数据,再加块/查询。发送数据时,直接遍历n个点,挨个发送的效率太低;可以这样考虑,只有被更新了的点才会向其他节点发送数据,所以只保存被更新了的点即可。我的代码是定义结构体,每个时间点有一个队列保存着该时刻会发送给邻居的数据。

【代码】

#include<bits/stdc++.h>
using namespace std;

int n,m,t,k,L, u,v, a,b,c;
vector<int> graph[505]; //邻接表
vector<int> chain[505]; //每个节点的主链

struct DATA{   //节点发送的数据
    int point;
    vector<int> data;
    DATA(int p,vector<int> &d)
    {
        point=p;
        data.assign(d.begin(),d.end());
    }
};
queue<DATA> send[1010]; //时间i 发送数据

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&u,&v);
        if(u==v)continue;
        graph[u].push_back(v);
        graph[v].push_back(u);
    }
    scanf("%d%d",&t,&k);
    int now=0;
    while(k--)
    {
        scanf("%d%d",&a,&b);
        while(now<b)
        {
            now++;
            set<int>updated; //保存被更新了的点
            while(!send[now%1007].empty())
            {
                int i=send[now%1007].front().point;
                const vector<int>&data=send[now%1007].front().data;
                for(int p=0;p<graph[i].size();p++)
                {
                    int j=graph[i][p]; //i试图将data发送给j
                    if(data.size()>chain[j].size()
                       ||data.size()>0&&data.size()==chain[j].size()
                         &&data.back()<chain[j].back())
                    {
                        chain[j].assign(data.begin(),data.end());
                        updated.insert(j);
                    }
                }
                send[now%1007].pop();
            }
            for(set<int>::iterator it=updated.begin();it!=updated.end();it++)
                send[(now+t)%1007].push(DATA(*it,chain[*it]));  //now+t时刻,j的数据发出到邻接点
        }

        if(getchar()==' ') //new block
        {
            scanf("%d",&c);
            chain[a].push_back(c);
            send[(now+t)%1007].push(DATA(a,chain[a])); //点a发送数据
        }
        else //query
        {
            printf("%d 0",chain[a].size()+1);
            for(int i=0;i<chain[a].size();i++)
                printf(" %d",chain[a][i]);
            puts("");
        }
    }
}

201912-5 魔数 

【这个】除了暴力,没想到高效的解法。。。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪的期许

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值