2016 CCF-CSP 计算机职业资格认证考试 解题报告

前言

嘛,参加这次考试属于一连串比较机缘巧合的事情——

首先是当上了2016年度CCF优秀大学生的荣誉,这个奖颁发的同时也赠予了为期一年的CCF会员资格

而会员……是具有一次免费参加CCF计算机职业资格认证考试的机会(就算续费或者再次获得会员资格也只有这一次免费的机会)

即便是我已经两年没有敲过C++了,经常会敲成Python的代码习惯……

嘛,我就抱着不考白不考的精神——

去试试吧 ^_^


(毕竟期末复习周,也没太多时间可以整理代码,如下代码均为考场中敲出,各种没有注意的小细节请原谅QwQ,哦对了第三题的答案并不是全对的需要注意一下)


A. 中间数


题面

问题描述
试题编号:1
试题名称:中间数
时间限制:1.0s
内存限制:256.0MB
问题描述:
问题描述
  在一个整数序列 a 1a 2, …,  an中,如果存在某个数,大于它的整数数量等于小于它的整数数量,则称其为中间数。在一个序列中,可能存在多个下标不相同的中间数,这些中间数的值是相同的。
  给定一个整数序列,请找出这个整数序列的中间数的值。
输入格式
  输入的第一行包含了一个整数 n,表示整数序列中数的个数。
  第二行包含 n个正整数,依次表示 a 1a 2, …,  an
输出格式
  如果约定序列的中间数存在,则输出中间数的值,否则输出-1表示不存在中间数。
样例输入
6
2 6 5 6 3 5
样例输出
5
样例说明
  比5小的数有2个,比5大的数也有2个。
样例输入
4
3 4 6 7
样例输出
-1
样例说明
  在序列中的4个数都不满足中间数的定义。
样例输入
5
3 4 6 6 7
样例输出
-1
样例说明
  在序列中的5个数都不满足中间数的定义。
评测用例规模与约定
  对于所有评测用例,1 ≤  n ≤ 1000,1 ≤  ai ≤ 1000。

解题报告

题意是说,要找一个数,序列中比这个数大的数的个数与序列中比这个数小的数的个数相同,该数称作中间数

这个问题可以等效看作一个更简单的问题模型:对于排序后数列最中间的数,向左向右移动两个pivot至第一个非该数的位置,倘若这两个pivot两侧的数字个数相同,则该数为中间数,反之为不是(简单的四则运算23333)。

#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define MAXN 1024

int a[MAXN];

int main()
{
	//freopen("A.txt","r",stdin);
	int n; scanf("%d",&n);
	map<int,int> cnt;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i-1];
		cnt[a[i-1]]++;
	}
	sort(a,a+n);
	int l = n>>1, r = n>>1, mid = a[n>>1];
	while(l>=0 && a[l]==mid)l--;
	while(r< n && a[r]==mid)r++;
	//cout<<l<<":"<<r<<endl;
	if(l+r == n-1) cout<<mid<<endl;
	else cout<<"-1"<<endl;
	return 0;
}



B. 工资计算


题面

问题描述
试题编号:2
试题名称:工资计算
时间限制:1.0s
内存限制:256.0MB
问题描述:
问题描述
  小明的公司每个月给小明发工资,而小明拿到的工资为交完个人所得税之后的工资。假设他一个月的税前工资(扣除五险一金后、未扣税前的工资)为 S元,则他应交的个人所得税按如下公式计算:
  1) 个人所得税起征点为3500元,若 S不超过3500,则不交税,3500元以上的部分才计算个人所得税,令 A= S-3500元;
  2)  A中不超过1500元的部分,税率3%;
  3)  A中超过1500元未超过4500元的部分,税率10%;
  4)  A中超过4500元未超过9000元的部分,税率20%;
  5)  A中超过9000元未超过35000元的部分,税率25%;
  6)  A中超过35000元未超过55000元的部分,税率30%;
  7)  A中超过55000元未超过80000元的部分,税率35%;
  8)  A中超过80000元的部分,税率45%;
  例如,如果小明的税前工资为10000元,则 A=10000-3500=6500元,其中不超过1500元部分应缴税1500×3%=45元,超过1500元不超过4500元部分应缴税(4500-1500)×10%=300元,超过4500元部分应缴税(6500-4500)×20%=400元。总共缴税745元,税后所得为9255元。
  已知小明这个月税后所得为 T元,请问他的税前工资 S是多少元。
输入格式
  输入的第一行包含一个整数 T,表示小明的税后所得。所有评测数据保证小明的税前工资为一个整百的数。
输出格式
  输出一个整数 S,表示小明的税前工资。
样例输入
9255
样例输出
10000
评测用例规模与约定
  对于所有评测用例,1 ≤  T ≤ 100000。

解题报告

可以先用正向计算的方法得出(注释里的代码)各个收入水平的税后价格进行一一对应

对于每个税后价格就可以直接知道该税后价格是在哪个税前价格的区间里,大大减少了尝试所花费的时间和空间

需要注意的一个要点是:A是除去无税收入的,结果别忘了加回来

#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define MAXN 100024

int rate[8]  = {0,    3,   10,   20,    25,    30,    35,     45};
int rank[8]  = {0, 1500, 4500, 9000, 35000, 55000, 80000, 154400};
int limit[8] = {0, 1455, 4155, 7755, 27255, 41255, 57505, 100000};

//	limit[i] = limit[i-1]  \
//	+( rank[i]-rank[i-1] ) \
//	-( rank[i]-rank[i-1] ) * rate[i]/100;

int main()
{
	int t; cin>>t;
	int a = t - 3500;
	
	if(a<=0)
	{
		cout<<t<<endl; 
		return 0;
	}
	
	for(int i=1; i<8; i++)
	{
		if(a <= limit[i])
		{
			int rest = a - limit[i-1];
			// Using integer calculation to avoid float_point error
			int ret = rank[i-1] + rest*100/(100-rate[i]);
			cout<< ret + 3500 <<endl;
			return 0;
		}
	}
	
	return 0;
}


C. 权限查询


题面

问题描述
试题编号:3
试题名称:权限查询
时间限制:1.0s
内存限制:256.0MB
问题描述:
问题描述
  授权 (authorization) 是各类业务系统不可缺少的组成部分,系统用户通过授权机制获得系统中各个模块的操作权限。
  本题中的授权机制是这样设计的:每位用户具有若干角色,每种角色具有若干权限。例如,用户 david 具有 manager 角色,manager 角色有 crm:2 权限,则用户 david 具有 crm:2 权限,也就是 crm 类权限的第 2 等级的权限。
  具体地,用户名和角色名称都是由小写字母组成的字符串,长度不超过 32。权限分为分等级权限和不分等级权限两大类。分等级权限由权限类名和权限等级构成,中间用冒号“:”分隔。其中权限类名也是由小写字母组成的字符串,长度不超过 32。权限等级是一位数字,从 0 到 9,数字越大表示权限等级越高。系统规定如果用户具有某类某一等级的权限,那么他也将自动具有该类更低等级的权限。例如在上面的例子中,除 crm:2 外,用户 david 也具有 crm:1 和 crm:0 权限。不分等级权限在描述权限时只有权限类名,没有权限等级(也没有用于分隔的冒号)。
  给出系统中用户、角色和权限的描述信息,你的程序需要回答多个关于用户和权限的查询。查询可分为以下几类:
  * 不分等级权限的查询:如果权限本身是不分等级的,则查询时不指定等级,返回是否具有该权限;
  * 分等级权限的带等级查询:如果权限本身分等级,查询也带等级,则返回是否具有该类的该等级权限;
  * 分等级权限的不带等级查询:如果权限本身分等级,查询不带等级,则返回具有该类权限的等级;如果不具有该类的任何等级权限,则返回“否”。
输入格式
  输入第一行是一个正整数  p,表示不同的权限类别的数量。紧接着的  p 行被称为 P 段,每行一个字符串,描述各个权限。对于分等级权限,格式为 <category>-<level>,其中 <category> 是权限类名,<level> 是该类权限的最高等级。对于不分等级权限,字符串只包含权限类名。
  接下来一行是一个正整数  r,表示不同的角色数量。紧接着的  r 行被称为 R 段,每行描述一种角色,格式为
  <role> <s> <privilege 1> <privilege 2> ... <privilege s>
  其中 <role> 是角色名称,<s> 表示该角色具有多少种权限。后面 <s> 个字符串描述该角色具有的权限,格式同 P 段。
  接下来一行是一个正整数  u,表示用户数量。紧接着的  u 行被称为 U 段,每行描述一个用户,格式为
  <user> <t> <role 1> <role 2> ... <role t>
  其中 <user> 是用户名,<t> 表示该用户具有多少种角色。后面 <t> 个字符串描述该用户具有的角色。
  接下来一行是一个正整数  q,表示权限查询的数量。紧接着的  q 行被称为 Q 段,每行描述一个授权查询,格式为 <user> <privilege>,表示查询用户 <user> 是否具有 <privilege> 权限。如果查询的权限是分等级权限,则查询中的 <privilege> 可指定等级,表示查询该用户是否具有该等级的权限;也可以不指定等级,表示查询该用户具有该权限的等级。对于不分等级权限,只能查询该用户是否具有该权限,查询中不能指定等级。
输出格式
  输出共  q 行,每行为 false、true,或者一个数字。false 表示相应的用户不具有相应的权限,true 表示相应的用户具有相应的权限。对于分等级权限的不带等级查询,如果具有权限,则结果是一个数字,表示该用户具有该权限的(最高)等级。如果用户不存在,或者查询的权限没有定义,则应该返回 false。
样例输入
3
crm:2
git:3
game
4
hr 1 crm:2
it 3 crm:1 git:1 game
dev 2 git:3 game
qa 1 git:2
3
alice 1 hr
bob 2 it qa
charlie 1 dev
9
alice game
alice crm:2
alice git:0
bob git
bob poweroff
charlie game
charlie crm
charlie git:3
malice game
样例输出
false
true
false
2
false
true
false
true
false
样例说明
  样例输入描述的场景中,各个用户实际的权限如下:
  * 用户 alice 具有 crm:2 权限
  * 用户 bob 具有 crm:1、git:2 和 game 权限
  * 用户 charlie 具有 git:3 和 game 权限
  * 用户 malice 未描述,因此不具有任何权限
评测用例规模与约定
  评测用例规模:
  * 1 ≤  pru ≤ 100
  * 1 ≤  q ≤ 10 000
  * 每个用户具有的角色数不超过 10,每种角色具有的权限种类不超过 10
  约定:
  * 输入保证合法性,包括:
  1) 角色对应的权限列表(R 段)中的权限都是之前(P 段)出现过的,权限可以重复出现,如果带等级的权限重复出现,以等级最高的为准
  2) 用户对应的角色列表(U 段)中的角色都是之前(R 段)出现过的,如果多个角色都具有某一分等级权限,以等级最高的为准
  3) 查询(Q 段)中的用户名和权限类名不保证在之前(U 段和 P 段)出现过
  * 前 20% 的评测用例只有一种角色
  * 前 50% 的评测用例权限都是不分等级的,查询也都不带等级

解题报告

唔,恕我直言这是不是大模拟?
实现难度不大,主要是敲之前需要事先想好合适的存储数据结构,我就是原先的结构感觉太罗嗦删了重写来着QvQ
将(user, category)作为主key,权限等级作为value
关于作为value的权限等级,此处我做了如下改动:
· 该category有等级时,value为等级+1,即0-9级的value为1-10
· 该category无等级时,具有权限的value为-1
· 该category不具有权限时,value为0,因为default的默认值是0
使用两个map进行存储,分别为Role-Authority与User-Authority,先对RA进行更新,然后对于每个User,遍历其拥有的Role更新该User的每项权限,取权限较高的值

#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

// Define Un-leveled is -1; level +1; Ignored 0;
typedef pair<string, string> pss;
typedef map<string, int> psi;
typedef map<pss, int> mpi;

mpi ra; //roleAuthor;
mpi ua; //userAuthor;

#define MAXN 233
#define MP(a,b) make_pair(a,b)

void update(string user, string role, string cate)
{
	int original_level = ua[make_pair(user, cate)];
	int current_level = ra[make_pair(role, cate)];
	ua[make_pair(user, cate)] = (
		abs(current_level) >= abs(original_level)?
		current_level : original_level
	);
	// cout<<"Update "<<user<<":"<<role<<":"<<cate<<endl;
	// cout<<original_level<<" "<<current_level<<endl;
}

int split(string s, char delim = ' ')
{
	int ret = s.find(delim);
	return ret;
}

int main()
{
	//freopen("C.txt","r",stdin);
	string str;
	int i,j,k;
	// pss testPair = make_pair("chendian", "CCFTest");
	// cout << author[testPair]; // Initial is 0
	
	int p; cin>>p;
	string anames[MAXN];
	for(i=0;i<p;i++)
	{
		cin>>str;
		int sp = split(str,':');
		//cout<<str<<" -> "<<split(str,':')<<endl;
		if (sp != -1) anames[i] = str.substr(0,sp);
		else anames[i] = str;
	}
	
	// R-Para
	int r; cin>>r;
	string rname; //roleName
	for(i=0;i<r;i++)
	{
		int s;
		cin>>rname>>s;
		for(j=0;j<s;j++)
		{
			cin>>str;
			int level = 0;
			int sp = split(str,':');			
			string category = "";
			if (sp != -1)
			{
				category = str.substr(0,sp);
				level = str.substr(sp+1)[0] - '0';
				// cout << category <<":"<< level << endl;
				ra[make_pair(rname, category)] = level+1;
			}
			else
			{
				category = str; // cate
				ra[make_pair(rname, category)] = -1;
			}
		}
	}
	
	int u; cin>>u;
	string uname; //userName
	for(i=0;i<u;i++)
	{
		int s;
		cin>>uname>>s;
		for(j=0;j<s;j++)
		{
			cin>>str; // role
			for(k=0;k<p;k++)
			{
				update(uname, str, anames[k]);
				// cout<<uname<<":"<<anames[k]<<":"<<ua[make_pair(uname, anames[k])]<<endl;
			}
		}
	}
	
	// Q-Para
	int q; cin>>q;
	string qname; //queryName
	for(i=0;i<q;i++)
	{
		cin>>qname>>str;
		int sp = split(str,':');
		string cate = "";
		cate = (sp != -1)?str.substr(0,sp):str;
		// cout<<qname<<":"<<cate<<endl;
		if(sp == -1)
		{
			int lev = ua[make_pair(qname,cate)];
			if(lev==0) cout<<"false"<<endl;
			else if(lev==-1) cout<<"true"<<endl;
			else cout<<lev-1<<endl;
		}
		else
		{
			int lev = ua[make_pair(qname,cate)]-1;
			int qlevel = str.substr(sp+1)[0]-'0';
			//cout<<lev<<":"<<qlevel<<endl;
			if(lev>=qlevel) cout<<"true"<<endl;
			else cout<<"false"<<endl;
		}
	}
	
	return 0;
}





D. 压缩编码


题面

问题描述
试题编号:4
试题名称:压缩编码
时间限制:3.0s
内存限制:256.0MB
问题描述:
问题描述
  给定一段文字,已知单词 a 1a 2, …,  an出现的频率分别 t 1t 2, …,  tn。可以用01串给这些单词编码,即将每个单词与一个01串对应,使得任何一个单词的编码(对应的01串)不是另一个单词编码的前缀,这种编码称为前缀码。
  使用前缀码编码一段文字是指将这段文字中的每个单词依次对应到其编码。一段文字经过前缀编码后的长度为:
   L= a 1的编码长度× t 1+ a 2的编码长度× t 2+…+  an的编码长度× tn
  定义一个前缀编码为字典序编码,指对于1 ≤  nai的编码(对应的01串)的字典序在 ai +1编码之前,即 a 1a 2, …,  an的编码是按字典序升序排列的。
  例如,文字E A E C D E B C C E C B D B E中, 5个单词A、B、C、D、E出现的频率分别为1, 3, 4, 2, 5,则一种可行的编码方案是A:000, B:001, C:01, D:10, E:11,对应的编码后的01串为1100011011011001010111010011000111,对应的长度 L为3×1+3×3+2×4+2×2+2×5=34。
  在这个例子中,如果使用哈夫曼(Huffman)编码,对应的编码方案是A:000, B:01, C:10, D:001, E:11,虽然最终文字编码后的总长度只有33,但是这个编码不满足字典序编码的性质,比如C的编码的字典序不在D的编码之前。
  在这个例子中,有些人可能会想的另一个字典序编码是A:000, B:001, C:010, D:011, E:1,编码后的文字长度为35。
  请找出一个字典序编码,使得文字经过编码后的长度 L最小。在输出时,你只需要输出最小的长度 L,而不需要输出具体的方案。在上面的例子中,最小的长度 L为34。
输入格式
  输入的第一行包含一个整数 n,表示单词的数量。
  第二行包含 n个整数,用空格分隔,分别表示 a 1a 2, …,  an出现的频率,即 t 1t 2, …,  tn。请注意 a 1a 2, …,  an具体是什么单词并不影响本题的解,所以没有输入 a 1a 2, …,  an
输出格式
  输出一个整数,表示文字经过编码后的长度 L的最小值。
样例输入
5
1 3 4 2 5
样例输出
34
样例说明
  这个样例就是问题描述中的例子。如果你得到了35,说明你算得有问题,请自行检查自己的算法而不要怀疑是样例输出写错了。
评测用例规模与约定
  对于30%的评测用例,1 ≤  n ≤ 10,1 ≤  ti ≤ 20;
  对于60%的评测用例,1 ≤  n ≤ 100,1 ≤  ti ≤ 100;
  对于100%的评测用例,1 ≤  n ≤ 1000,1 ≤  ti ≤ 10000。

解题报告

个人当时的想法先是用树状数组维护初始值已全部设为1的叶子节点,
使用DP维护OPT[lef,rig],初始化为OPT[i,i]=1,
自底向上的更新中间节点的最小值,对于OPT[lef,rig],遍历[lef,rig]的分割点,左右两个子问题的最优解加上合并消耗,和当前OPT比较更新
更新完整张图即可得到答案。
不过尚未实现,不知道对不对就是了(*^__^*) 

结语

这次只能对陪伴我前三年大学生活的C++说声真的很抱歉了……QAQ

第五题的级数收敛由于本人数学的问题看不懂题意……QwQ

第四题没能来得及写完……OvO

前三题的第三题在交卷前五分钟才想起来注释掉freopen,即便如此也有一组测试数据没能通过……=v=

成绩如下,待MachineLearning学得小有所成,再回来继续玩CF和TC……^_^

C++等着我~~





### 回答1: CCF-CSP(中国计算机学会认证系统程序员)认证考试是由中国计算机学会主办的,旨在选拔高水平的系统程序员和开发人员。该认证考试包括两个级别:初级和高级。初级考试主要测试候选人的基础知识和编程能力,高级考试则主要测试候选人的系统设计和开发能力。CCF-CSP认证考试的评分标准严格,要求考生表现出扎实的计算机理论基础和熟练的编程技巧。 为了顺利通过CCF-CSP认证考试,考生需要掌握扎实的计算机基础知识和熟练的编程技巧,并且需要在考试前进行充分的准备。以下是一些复习技巧指导: 1. 确认考试大纲 考生需要仔细阅读考试大纲,了解考试范围和题型。要特别注意考试大纲中的重点内容,重点复习。 2. 提前规划复习进度 准备CCF-CSP认证考试需要时间,而时间是很宝贵的,考生需要提前规划好复习进度和复习内容,尽可能利用零散时间进行复习。 3. 多做题 要想掌握CCF-CSP认证考试所需的知识和技能,考生需要多做题,并针对性地练习一些经典的考题。在练习中,考生需要注重练习思路和解题方法,该方法可以加深对知识点的理解。 4. 关注考试动态 考试动态指的是考试历年来的趋势和变化,考生需要不断关注考试动态,了解考试趋势,及时更新复习内容和方法,以便更好地应对考试。 总之,要想通过CCF-CSP认证考试,考生需要理论基础和开发经验充足,并且需要在复习时切实遵循学科规范,灵活应用知识点,较好地掌握考试技巧,这是顺利通过CCF-CSP认证考试的关键。 ### 回答2: ccf-csp认证考试是中国计算机学会(CCF认证的一个计算机软件专业技能等级考试,也是中国IT行业里非常重要的证书之一。该考试分为初级、中级和高级三个等级,其中初级考试包含了基础的程序设计和算法等内容,中级考试中主要考察了软件工程、数据结构、算法设计和网络安全等领域的知识,而高级考试则更注重的是对软件工程和软件项目管理的理解和应用,以及对复杂系统的设计和实现方法的掌握。为了能够成功通过ccf-csp认证考试,首先需要在考前充分准备,并且需要有一些好的复习技巧,这里向大家推荐以下几点: 1.学好基础知识。初级、中级和高级csp认证考试所涉及到的内容都是建立在一定的基础知识上的。由于考试的难度逐级递增,所以建议在准备考试之前先花时间学好基础知识。 2.多做练习题。做练习题能够帮助我们加深对知识的理解,熟悉考试形式和规则,同时也能提高我们的答题速度和准确度。 3.注意时间管理。不同级别的csp认证考试都有时限,在考试过程中需要注意时间的分配。建议在每次做练习题的时候都设置一个时间限制,并逐步缩短时间限制,以提高答题效率。 4.了解考试规则。在考前一定要熟悉考试规则和题型,知道考试时可以携带哪些资料和工具,了解考试的答题和评分规则等。 总之,ccf-csp认证考试是一项非常重要的认证考试,通过这个认证不仅可以增加个人的职业竞争力,还能证明个人在软件开发和IT行业方面的专业能力。因此,我们需要认真地准备和复习,字斟句酌地做好每一个细节,以确保顺利通过考试。 ### 回答3: ccf-csp认证考试是由中国计算机学会主办的全国性计算机考试考试内容涵盖了计算机应用、技术、算法等多个方面,分为初级、中级和高级三个等级。参加此考试不仅有助于检测个人的计算机专业水平,更能提升自身的竞争力和职业发展前景。 为了通过ccf-csp认证考试,考生需要重点复习考试要求中的各个内容模块,如数据类型、运算符、控制结构、数组、函数、文件操作等程序设计相关方面;还包括计算机网络、操作系统、数据库等计算机技术类知识;另外,还需要掌握常见算法和数据结构,如排序、查找、树等。此外,考生还需要通过大量的编程练习和模拟题练习,熟悉考试题型和答题技巧,提升自己的完成效率和准确性。 在复习过程中,需要注重细节和实践。例如,对于程序设计相关方面,需要理解每个知识点的原理和应用场景,在编写代码时要注重细节避免出现错误。对于算法和数据结构方面,需要熟悉常见算法的实现方式和优缺点,掌握好算法的时间复杂度和空间复杂度,做到熟练掌握。 总之,通过充分准备和钻研,考生有望成功通过ccf-csp认证考试,展现自己的计算机专业能力和职业素养。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

糖果天王

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

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

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

打赏作者

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

抵扣说明:

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

余额充值