【缘起】
从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 魔数
【这个】除了暴力,没想到高效的解法。。。
推荐阅读
- 打工|为什么iPhone、ipad一直用Lightning而不用type-c()
- windows|微软改主意了(Windows Server提供WSL2支持)
- 有趣的论文|多模态逆天图片生成,OpenAI又一力作(DALL·E 2)