选择题

目录

1058:选择题

输入格式:

输出格式:

输入样例:

输出样例:

代码长度限制:

时间限制:

内存限制:

思路:

  1.选择题结构体

  1.2 选择题结构体代码

  2.判断选择题是否做对函数

  2.1 判断选择题是否做对函数代码:

  3.选择题的输入

  3.1 选择题的输入代码:

  4.学生做选择题的输入

  4.1 学生做选择题的输入代码

  5.批改学生做的选择题

  5.1 批改学生做的选择题代码:

  6. 输出每一个学生获得的总分数

  6.1 输出每一个学生获得的总分数代码:

  7.统计出错最多的选择题

    7.1 结构体排序规则1

    7.1.1 结构体排序规则1代码:

    7.2 结构体排序规则2

      7.2.1 统计有多少个错题数量相同的选择题

      7.2.2 统计有多少个错题数量相同的选择题代码:

  7.2.3 结构体排序规则2代码:

  8.输出错题数量最多的选择题编号

  8.1 输出错题数量最多的选择题编号代码:

总代码:

该算法的复杂度:

  1.时间复杂度

  2.空间复杂度

总结:

题目链接:


1058:选择题

批改多选题是比较麻烦的事情,本题就请你写个程序帮助老师批改多选题,并且指出哪道题错的人最多。

输入格式:

输入在第一行给出两个正整数 N(≤ 1000)和 M(≤ 100),分别是学生人数和多选题的个数。随后 M 行,每行顺次给出一道题的满分值(不超过 5 的正整数)、选项个数(不少于 2 且不超过 5 的正整数)、正确选项个数(不超过选项个数的正整数)、所有正确选项。注意每题的选项从小写英文字母 a 开始顺次排列。各项间以 1 个空格分隔。最后 N 行,每行给出一个学生的答题情况,其每题答案格式为 (选中的选项个数 选项1 ……),按题目顺序给出。注意:题目保证学生的答题情况是合法的,即不存在选中的选项数超过实际选项数的情况。

输出格式:

按照输入的顺序给出每个学生的得分,每个分数占一行。注意判题时只有选择全部正确才能得到该题的分数。最后一行输出错得最多的题目的错误次数和编号(题目按照输入的顺序从 1 开始编号)。如果有并列,则按编号递增顺序输出。数字间用空格分隔,行首尾不得有多余空格。如果所有题目都没有人错,则在最后一行输出 Too simple

输入样例:

3 4 
3 4 2 a c
2 5 1 b
5 3 2 b c
1 5 4 a b d e
(2 a c) (2 b d) (2 a c) (3 a b e)
(2 a c) (1 b) (2 a b) (4 a b d e)
(2 b d) (1 e) (2 b c) (4 a b c d)

输出样例:

3
6
5
2 2 3 4

代码长度限制:

16 KB

时间限制:

300 ms

内存限制:

64 MB

思路:

  1.选择题结构体

  这道题看起来非常的复杂,首先我们需要写一个关于构建选择题的结构,这个结构需要一些什么呢?首先就是这个选择题的编号,我们用b来表示.

  然后就是这个选择题做对了之后的分数,我们用f来表示.

  还有就是这个选择题的选项总数,我们可以用s来进行表示.

  之后就是这个选择题里面正确选项的数量,我们用z来进行表示.

  在之后就是这个选择题里面正确选项是什么,定义一个char类型的内存空间,用zfc来进行表示.

  在然后就是这个选择题被做错了的次数,我们可以用c来进行表示.

  最后我们可以定义一个初始化函数init,函数参数有_f,_s,_z,_zfc,_b,函数里面呢首先就给zfc分配一片长度为_z的内存空间,然后用下标将_zfc赋值到zfc里面.然后其他的参数都赋值过去,我们还需要将c初始化为0,代表刚开始有0个人做错这道选择题.

  1.2 选择题结构体代码

struct xz{ //选择题的结构体 
	int b; //这个选择题的编号 
	int f; //这个选择题做对过后获得的分数 
	int s; //这个选择题的选项总数 
	int z; //这个选择题的正确选项数量 
	char *zfc; //这个选择题的正确选项 
	int c; //这个选择题有多少个人做错 
	void init(int _f,int _s,int _z,char a[],int _b){ //初始化函数 
		zfc=new char[_z]; //分配一个长度为_z的内存空间 
		for(int i=0;i<_z;i++) //按下标赋值 
		  zfc[i]=a[i]; //每一个字符都进行赋值 
		f=_f; //分数赋值 
		s=_s; //选项总数赋值 
		z=_z; //正确选项数量赋值 
		b=_b; //选择题编号赋值 
		c=0; //将统计做错次数的计数器初始化为0 
	}
};

  2.判断选择题是否做对函数

  我们可以再写一个判断这个选择题是否做对的函数.

   函数参数为一个选择题结构xz a,学生在选择题里面选的选项的数组b,以及这个数组b的长度n.

  首先我们要判断a.z(这个选择题正确选项的个数)是否等于n,如果不等于,那就说明学生肯定选错了,返回false,毕竟选的数量都不对,如果等于的话,进行下一步判断.

  依次遍历a.zfc和b这两个字符串,如果有一个选错,那么就返回false,代表学生选错了,如果遍历一遍都没有找到不同的地方,就返回true,代表学生选对了.

  2.1 判断选择题是否做对函数代码:

bool pd(xz a,int n,char b[]){ //判断选择题是否做对函数代码
	if(a.z!=n) //看学生选的选项个数和正确选项个数是否不同 
	  return false; //如果不同,就代表学生选错了 
	else{ //如果相同,进行下一次判断 
		for(int i=0;i<n;i++) //依次遍历比较 
		  if(a.zfc[i]!=b[i]) //如果找到一个不相同的 
		    return false; //就代表学生选错了 
		return true; //没有找到不相同的地方,就代表学生做对了 
	}
}

  3.选择题的输入

    我们首先输入的是n(学生个数)和m(选择题个数),然后进行m次输入一个选择题的内容的循环,期间,定义三个整数f(这个选择题做对后的分数),x(这个选择题的选项总数),y(这个选择题的正确选项数量),输入过后,我们在定义一个char类型的数组s,长度为y(代表正确的选项有哪些).然后进行y次输入s[j],之后我们可以用xz这个结构体的初始化函数init进行一下初始化!

  3.1 选择题的输入代码:

	int n,m; //n为学生数量,m为选择题的数量 
	cin>>n>>m; //输入学生数量和选择题的数量 
	int sum[n]={0}; //每个学生获得的分数 
	xz a[m]; //m个选择题的信息 
	for(int i=0;i<m;i++){ //输入m个选择题的信息 
		int f,x,y; //f是这个选择题做对后获得的分数,x是这个选择题的选项总数,y是这个选择题的正确选项数目 
		cin>>f>>x>>y; //输入选择题的分数,选项总数,正确选项总数 
		char s[y]; //定义一个长度为y的数组,存储的是y个正确的选项 
		for(int j=0;j<y;j++) //依次输入y个正确的选项 
		  cin>>s[j]; //输入第j个正确的选项 
		a[i].init(f,x,y,s,i); //利用初始化函数进行赋值 
	}

  4.学生做选择题的输入

  因为题目的特殊输入要求,需要有括号在旁边,我们就需要定义一个新的char类型变量,用于存储左括号和右括号,每一个学生做完之后,输入的数据我们可以不用保留(也就是说我们不用定义一个三维的数组,只需要在for循环之中定义新的变量就可以了),然后在定义一个整数d(代表学生做这道题选的选项个数),输入了左括号和d之后,我们在定义一个长度为d的char类型数组l,里面存储的就是学生选的选项是哪些.

  之后我们进行d次循环,依次输入,把l给输入了之后,我们在输入一个右括号,这些输入完了之后,就可以进行判断学生是否做对,学生获得的分值,这道选择题的记录错误人数的计数器要不要了.(之后再说)!

  4.1 学生做选择题的输入代码

	while(n--){ //n个学生的试卷 
		for(int i=0;i<m;i++){ //做m道选择题的答案 
			char c; //刚开始的括号用这个输入 
			int d; //这个选择题学生选的个数 
			cin>>c>>d; //输入左括号和选择题学生选的个数 
			char l[d]; //定义一个长度为d的字符串,存储的是学生选的选项 
			for(int j=0;j<d;j++) //依次输入学生选的d个选项 
			  cin>>l[j]; //输入学生选的第j个选项 
			cin>>c; //输入右括号 
			/*
			......
			中间是判断做题是否正确加分的代码...... 
			......
			*/
		}
		/*
		......
		中间是判断做题是否正确加分的代码...... 
		......
		*/
	}

  5.批改学生做的选择题

  将每一个学生做的选择题选项输入了之后,就该批改学生做的选择题了(之前吗定义了一个sum数组,就是用来存储这些学生获得的分数的),之前我们也写了一个判断是否做对了的函数pd,我们现在就可以运用进来.

  首先定义一个bool类型的变量f,将其赋值为pd(a[i],d,l)的返回值,如果为真(true)的话,代表做对了,将sum[x(x为最开始定义的一个下标,最大为n,代表着学生的编号)]的值在加上这个选选择题的分数(a[i].f).

  如果!f(取反f的值,比如说,如果f为true,取反之后为false,反之,如果f为false,取反之后就成了tue)的值为真(true)的话,代表学生这道选择题做错了,我们只需要将这个选择题记录错题人数(a[i].c)的变量进行+1的操作.

  之后输入完一个学生的所有做选择题的for循环之后,将记录学生当前的下标的x进行+1操作(毕竟下一次就是下一个同学的卷子了).

  5.1 批改学生做的选择题代码:

	while(n--){ //n个学生的试卷 
		for(int i=0;i<m;i++){ //做m道选择题的答案 
			char c; //刚开始的括号用这个输入 
			int d; //这个选择题学生选的个数 
			cin>>c>>d; //输入左括号和选择题学生选的个数 
			char l[d]; //定义一个长度为d的字符串,存储的是学生选的选项 
			for(int j=0;j<d;j++) //依次输入学生选的d个选项 
			  cin>>l[j]; //输入学生选的第j个选项 
			cin>>c; //输入右括号 
			bool f=pd(a[i],d,l); //判断是否做对了这道选择题 
			if(!f) //如果取反后为true,就代表f是false,学生这道选择题做错了 
			  a[i].c++; //将记录错题人数的计数器c进行+1操作 
			else //如果取反后不是true,就代表f是true,学生这道选择题做对了 
			  sum[x]+=a[i].f; //将记录学生分数的数组加上这个选择题的分数 
		}
		x++; //学生下标增加 
	}

  6. 输出每一个学生获得的总分数

  经过上述批改学生做选择题卷子的操作之后,sum这个数组里面就存的是每一个学生做选择题获得的总分数,我们就只需要for循环输出一遍就行了.

  6.1 输出每一个学生获得的总分数代码:

	for(int i=0;i<x;i++) //将每一个学生获得的总分数输出出来 
	  cout<<sum[i]<<endl; //输出编号为i的学生的总分数 

  7.统计出错最多的选择题

    7.1 结构体排序规则1

  我们首先需要进行一个排序,为了简单一些,吗可以用C++STL库系统默认提供的sort排序函数,但是我们需要排序的是一个结构,系统不知道我们的结构要怎么排序,所以会报错.

  现在吗就需要写一个结构体排序规则的函数,我们呢需要出错最多的选择题,然后输出一次出错次数,接着输出他们的编号,我们的规则就可以制定为按照两个选择题xz结构体中的变量c进行比较,谁的大,谁就在前面.

    7.1.1 结构体排序规则1代码:

bool cmp(xz a,xz b){ //参数是两个结构体 
	return a.c>b.c; //比较这两个选择题错题数量谁多 
}

    7.2 结构体排序规则2

  题目要求说了,可能有多个错题数量相同的选择题,按照他们的编号进行从小到大依次输出,经过第一次排序过后,多个错题数量相同的选择题的编号可能不是从小到大,这样我们就需要在进行一次按照编号来排序了.


      7.2.1 统计有多少个错题数量相同的选择题

  我们要排序,就要给出排序的长度,第二次排序,排序的长度就是错题数量相同的选择题数量我们可以怎么求呢?

  我们知道,经过第一次排序过后,下标为0的选择题错题的数量绝对是最多的,我们就可以进行循环遍历,从0开始,到m-1,中间怎么判断呢?可以定义一个计数器vum初始化为0,如果相等就将计数器+1,如果不相等,那么后面的数字肯定也不相等,就可以直接break退出.

      7.2.2 统计有多少个错题数量相同的选择题代码:

	for(int i=0;i<m;i++){  //统计有多少个错题数量相同的选择题
	    if(a[0].c==a[i].c) //如果找到相同的 
	      vum++; //就将计数器进行+1操作 
	    else //如果找到一个不相同的,那就说明后面的肯定不会相同 
	      break; //就可以直接退出了 
	}

  经过上述操作之后,知道了第二次排序的长度应该是vum,现在我们可以写一个第二次排序规则的函数了,我们是根据编号比较的,就可以按照两个选择题xz结构中的变量b来进行比较,谁的编号小,谁就往前面去.

  7.2.3 结构体排序规则2代码:

bool cmp2(xz a,xz b){ //参数是两个结构体 
	return a.b<b.b; //比较这两个选择题的编号谁的更小 
}

  8.输出错题数量最多的选择题编号

  因为题目有特殊要求,行末没有多余的空格,所以需要进行一次特判,如果不是最后一行,就多输出一个空格,如果是,就多输出一个换行键(<<endl;)!

  8.1 输出错题数量最多的选择题编号代码:

	sort(a,a+m,cmp); //按照每一个选择题的错题次数排序 
	for(int i=0;i<m;i++){  //统计有多少个错题数量相同的选择题
	    if(a[0].c==a[i].c) //如果找到相同的 
	      vum++; //就将计数器进行+1操作 
	    else //如果找到一个不相同的,那就说明后面的肯定不会相同 
	      break; //就可以直接退出了 
	}
	sort(a,a+vum,cmp2); //按照每一个错题数量相同的选择题的编号进行排序 
	cout<<a[0].c<<" "; //输出最多的错题数量 
	for(int i=0;i<vum;i++){ //输出错题数量最多的选择题的编号 
		if(i!=(vum-1)) //如果不是最后一个 
		  cout<<a[i].b+1<<" "; //输出空格 
		else //如果是最后一个 
		  cout<<a[i].b+1<<endl; //不输出空格,输出换行 
	}

总代码:

#include<bits/stdc++.h>
using namespace std;
struct xz{ //选择题的结构体 
	int b; //这个选择题的编号 
	int f; //这个选择题做对过后获得的分数 
	int s; //这个选择题的选项总数 
	int z; //这个选择题的正确选项数量 
	char *zfc; //这个选择题的正确选项 
	int c; //这个选择题有多少个人做错 
	void init(int _f,int _s,int _z,char a[],int _b){ //初始化函数 
		zfc=new char[_z]; //分配一个长度为_z的内存空间 
		for(int i=0;i<_z;i++) //按下标赋值 
		  zfc[i]=a[i]; //每一个字符都进行赋值 
		f=_f; //分数赋值 
		s=_s; //选项总数赋值 
		z=_z; //正确选项数量赋值 
		b=_b; //选择题编号赋值 
		c=0; //将统计做错次数的计数器初始化为0 
	}
};
bool cmp(xz a,xz b){ //参数是两个结构体 
	return a.c>b.c; //比较这两个选择题错题数量谁多 
}
bool cmp2(xz a,xz b){ //参数是两个结构体 
	return a.b<b.b; //比较这两个选择题的编号谁的更小 
}
bool pd(xz a,int n,char b[]){ //判断选择题是否做对函数代码
	if(a.z!=n) //看学生选的选项个数和正确选项个数是否不同 
	  return false; //如果不同,就代表学生选错了 
	else{ //如果相同,进行下一次判断 
		for(int i=0;i<n;i++) //依次遍历比较 
		  if(a.zfc[i]!=b[i]) //如果找到一个不相同的 
		    return false; //就代表学生选错了 
		return true; //没有找到不相同的地方,就代表学生做对了 
	}
}
int main(){
	int n,m; //n为学生数量,m为选择题的数量 
	cin>>n>>m; //输入学生数量和选择题的数量 
	int sum[n]={0}; //每个学生获得的分数 
	xz a[m]; //m个选择题的信息 
	for(int i=0;i<m;i++){ //输入m个选择题的信息 
		int f,x,y; //f是这个选择题做对后获得的分数,x是这个选择题的选项总数,y是这个选择题的正确选项数目 
		cin>>f>>x>>y; //输入选择题的分数,选项总数,正确选项总数 
		char s[y]; //定义一个长度为y的数组,存储的是y个正确的选项 
		for(int j=0;j<y;j++) //依次输入y个正确的选项 
		  cin>>s[j]; //输入第j个正确的选项 
		a[i].init(f,x,y,s,i); //利用初始化函数进行赋值 
	}
	int x=0,vum=0;
	while(n--){ //n个学生的试卷 
		for(int i=0;i<m;i++){ //做m道选择题的答案 
			char c; //刚开始的括号用这个输入 
			int d; //这个选择题学生选的个数 
			cin>>c>>d; //输入左括号和选择题学生选的个数 
			char l[d]; //定义一个长度为d的字符串,存储的是学生选的选项 
			for(int j=0;j<d;j++) //依次输入学生选的d个选项 
			  cin>>l[j]; //输入学生选的第j个选项 
			cin>>c; //输入右括号 
			bool f=pd(a[i],d,l); //判断是否做对了这道选择题 
			if(!f) //如果取反后为true,就代表f是false,学生这道选择题做错了 
			  a[i].c++; //将记录错题人数的计数器c进行+1操作 
			else //如果取反后不是true,就代表f是true,学生这道选择题做对了 
			  sum[x]+=a[i].f; //将记录学生分数的数组加上这个选择题的分数 
		}
		x++; //学生下标增加 
	}
	for(int i=0;i<x;i++) //将每一个学生获得的总分数输出出来 
	  cout<<sum[i]<<endl; //输出编号为i的学生的总分数 
	sort(a,a+m,cmp); //按照每一个选择题的错题次数排序 
	for(int i=0;i<m;i++){  //统计有多少个错题数量相同的选择题
	    if(a[0].c==a[i].c) //如果找到相同的 
	      vum++; //就将计数器进行+1操作 
	    else //如果找到一个不相同的,那就说明后面的肯定不会相同 
	      break; //就可以直接退出了 
	}
	sort(a,a+vum,cmp2); //按照每一个错题数量相同的选择题的编号进行排序 
	cout<<a[0].c<<" "; //输出最多的错题数量 
	for(int i=0;i<vum;i++){ //输出错题数量最多的选择题的编号 
		if(i!=(vum-1)) //如果不是最后一个 
		  cout<<a[i].b+1<<" "; //输出空格 
		else //如果是最后一个 
		  cout<<a[i].b+1<<endl; //不输出空格,输出换行 
	}
	return 0; //结束 
}

该算法的复杂度:

  1.时间复杂度

  我们先来计算时间复杂度,可以看出来,我们这里的每一个操作都涉及了预处理(在输入的时候进行处理).

  首先,输入每一个选择题的信息的时候,进行了m次输入,里面有一次init的初始化函数操作,init函数里面有一个for循环,复杂度是O(Z(Z是正确选项个数,不超过5)),那么输入每一个选择题信息的复杂度为O(ZM).

  然后来看批改每一个学生的试卷,有n个同学,每一个同学做m个选择题,复杂度是O(NM),中间,还执行了一次判断选择题是否做对的函数,复杂度为O(logD(d为每一个选择题学生选的个数,最大也是不超过5)),所以批改同学试卷发时间复杂度为O(NMlogD);

  最后是输出时候的复杂度,输出每一个同学的分数循环,复杂度为O(N(while循环过后,x的值等于n)) ,然后是进行第一次排序,复杂度为O(M^2(sort是冒泡排序)).之后进行统计有多少个有多少个最大错题数相等的选择题数量,时间复杂度为O(logM);之后又进行了一次冒泡排序,复杂度为O(logM^2).

  所以说,我们的总复杂度是O(ZM+NMlogD+N+M^2+logM+logM^2).最后总结一下等于O(NMlogD+2M^2)(为了简略一下,忽略一下较为小的数),按照前面那个比较完整的时间复杂度,我们来计算一下最大时间复杂度是多少Z最大为5,M最大为100,N最大为1000,D最大为5,总复杂度最大为O(500+500000+1000+10000+100+10000)=O(521600).最大是五十二万一千六百,在160ms之前是算的出来的,所以不会超时.

  2.空间复杂度

  首先看xz这个结构的空间复杂度,最大大概为O(10),我们定义了M个这样的结构,空间复杂度为O(10M),然后又定义了数组sum,长度为n,所以它的空间复杂度是O(N).

  所以他的整体复杂度应该是O(10M+N)(省略了一些较小的空间)!

总结:

  这道题算是特别特别难的了,需要进行写特殊的结构体,以及多个结构体排序函数,还有分数记录和函数进行的判断.

题目链接:

PTA | 程序设计类实验辅助教学平台千名教师建设,万道高质量题目,百万用户拼题的程序设计实验辅助教学平台https://pintia.cn/problem-sets/994805260223102976/exam/problems/994805270356541440

推荐:

欢迎大家前往AcWing网站进行刷题,上课,该网站由yxc,y总建立!

AcWing一个专属于程序员的平台,为大家在漫漫的刷题之旅中,提供最优质的解答https://www.acwing.com/about/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

龙星尘

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

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

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

打赏作者

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

抵扣说明:

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

余额充值