CCF|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 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<

201912-2 回收站选址
【分析】
由于坐标过大,可利用C++之STL库的map(STL库网上有很多详解,不赘述)。map用法和python里的字典极为相似。该题map的key值是坐标(建议用struct{ int x,y; },我用的是STL库的pair,本质就是个结构体)。
输入坐标是,用map标记每个坐标为1,然后遍历坐标,判断八个方位的坐标情况(被标记为1的说明有坐标,否则无)。依题,先判断该点能否作为垃圾桶,然后统计斜四方的坐标存在个数。
延伸:其实本题用STL库set就足够了,没必要用map,因为map的值没用到,只需要判断是否存在。
【代码】
#include 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,int> mmp; cin>>n; for(int i=0; i(x[i],y[i])]=1; } for(int i=0; i(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(x[i]+dir[j][0],y[i]+dir[j][1]))!=mmp.end()) tot++; } ans[tot]++; } } for(int i=0; i<5; i++) cout<

201912-3 化学方程式
【分析】
【CCF|CCF CSP认证考试201912题解(1,2,3,4)】这题挺恶心的。。。借助map和string可以方便些。 由括号嵌套应该想到递归(或理解为树)的思想,每多一层,就要递归一层,括号内的内容可以当作全新的一个分子。 用map记录每种原子的数量,最终比较map所存的原子数量是否相等。
延伸:也可以不用map,每读到一种原子,就为其赋予一个数字编号id,如此就可以用数组代替上文的map。这样运行时间会短很多。
【代码】
#include using namespace std; typedef long long ll; int n; char str[1010]; map 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 &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&L,map&R) //比较两个map内容是否一致 { if(L.size()!=R.size())return false; //不一致 for(map::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; //系数默认为1if(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 using namespace std; int n,m,t,k,L, u,v, a,b,c; vector graph[505]; //邻接表 vector chain[505]; //每个节点的主链struct DATA{//节点发送的数据 int point; vector data; DATA(int p,vector &d) { point=p; data.assign(d.begin(),d.end()); } }; queue send[1010]; //时间i 发送数据int main() { scanf("%d%d",&n,&m); for(int i=0; iupdated; //保存被更新了的点 while(!send[now%1007].empty()) { int i=send[now%1007].front().point; const vector&data=https://www.it610.com/article/send[now%1007].front().data; for(int p=0; pchain[j].size() ||data.size()>0&&data.size()==chain[j].size() &&data.back()::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

201912-5 魔数
【这个】除了暴力,没想到高效的解法。。。

    推荐阅读