1073 多选题常见计分法 (20 分)(与 或 异或 解法)

1073 多选题常见计分法 (20 分)

题目

批改多选题是比较麻烦的事情,有很多不同的计分方法。有一种最常见的计分方法是:如果考生选择了部分正确选项,并且没有选择任何错误选项,则得到 50% 分数;如果考生选择了任何一个错误的选项,则不能得分。本题就请你写个程序帮助老师批改多选题,并且指出哪道题的哪个选项错的人最多。

输入格式:

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

输出格式:

按照输入的顺序给出每个学生的得分,每个分数占一行,输出小数点后 1 位。最后输出错得最多的题目选项的信息,格式为:错误次数 题目编号(题目按照输入的顺序从1开始编号)-选项号。如果有并列,则每行一个选项,按题目编号递增顺序输出;再并列则按选项号递增顺序输出。行首尾不得有多余空格。如果所有题目都没有人错,则在最后一行输出 Too simple。

输入样例 1:

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) (3 b d e) (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) (1 c) (4 a b c d)

输出样例 1:

3.5
6.0
2.5
2 2-e
2 3-a
2 3-b

输入样例 2:

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

输出样例 2:

5.0
5.0
Too simple

思路

这题和1058感觉有点相似,不过这里属实复杂了,1058用的投机取巧的方式判断对错,这题还要具体统计每个选项的情况,属实不能投机了,只能请柳神了!!
这题的核心就在于用什么方式判断正误,并且能够获取每个选项的对错情况这里用了 ^ 来判断是否完全正确,用 | 来判断是没有全对还是选错了,用 &来判断每一个选项的答题情况
下面来具体分析一下每一步是如何实现的

模拟一个案例
正确选项是 a c d
三名学生给出的答案是
甲a c d
乙a c
丙a e

将正确答案和学生的答案映射成二进制数取a为低位,这边可以更具自己的思路决定顺序,只要不乱就好
那么正确答案acd映射成01101
甲同学 01101
乙同学 00101
丙同学 10001

  • ^异或判断对错
    这边的异或是对每一位进行异或,如果相等则为0,反之为1那么如果正确答案和同学们给出的答案相同的话,异或的到的答案就是0了,这样我们就很容易在程序中进行判断和处理了

  • |或判断错误还是对一半
    同理 01101 | 00101 或是对每一位进行或运算,如果有一个是1那么就是为1,这样的话只有同学的答案中出现了正确答案中没有的选项,或才会改变正确答案中的值,如果是漏选的话,并不影响正确答案的值,那么我就就可以通过或完后的答案和正确答案进行比较,判断出是否出现错误的情况,但是或比并不能判断全对还是半对,所以需要先判断是否全对,在用或来判断,具体实现在代码中体现

  • & 与判断每一位的情况
    这里与的实现原理就更加好理解了,我们已经知道通过^异或之后如果出现有的位上出现1,那么说明这一位和正确答案不一致,也就代表着这个选项该同学选错了,我们就用1,2,4,8,16遍历异或后的结果找出选错的那些选项,并计数就可以了

知道如何判断对错的放法,那么剩下的是实现出来了,这题的变量还是很多的,非常容易出错,而且出错还不容易debug,一些步骤的已经在代码中注释了。

代码

#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
/*变量
n和m存人数和题目数
一个数组长度为题目数m存放正确答案 vector<int> trueopt(m) 
一个数组长度为5 用来将选项abcde转译成二进制数的中间数组 hash【5】 5个元素分别 1 2 4 8 16 对于5位二进制数的权值
 一个数组长度为题目数m的用来存放每道题的满分值 vector<int> fullscore(m) 
 一个二维数组 存放每道题的每个选项的错误次数 vector<vector<int> > cnt(m,vector<int>(5))
*/ 
int main(){
	int n,m;
	cin>>n>>m;
//开始读取题目的满分值和正确选项
	char c;
	int optnum,truenum;
	int hash[5] = {1, 2, 4, 8, 16};
	vector<int> trueopt(m),fullscore(m);
	for(int i = 0;i < m;i++){
		cin>>fullscore[i]>>optnum>>truenum;
		for(int j = 0;j < truenum;j++){
			cin>>c;
			trueopt[i] += hash[c - 'a'];
		}
	}
//开始读取每个同学每道题的答题情况,并在读取过程中进行核对和计算题目错误次数
 	vector<vector<int> > cnt(m,vector<int>(5));
 	int temp,opt;
	 for(int i = 0;i < n;i++){
	 	double grade = 0;
	 	for(int j = 0;j < m;j++){
	 		getchar();//读到每道题刚开始的时候要用的scanf的格式化输入过滤掉( 这时候必须要将前面的一个回车或者空格吃掉不然会读错
			scanf("(%d",&temp);
			opt = 0;
			for(int k = 0;k < temp;k++){
				scanf(" %c)",&c);
				opt += hash[c - 'a'];
			} 
			int el = opt ^ trueopt[j];
			if(el){//不完全正确 
				if((opt | trueopt[j]) == trueopt[j]){
					grade += fullscore[j] * 1.0 / 2;
				}
				for(int k = 0;k < 5;k++){
					if(el & hash[k]){//el的每一位如果是1的话就代表这一位所代表的选项错了,所以用hash遍历el找出错误的选项 
						cnt[j][k]++;
					}
				}
			}
			else{
				grade += fullscore[j];
			} 
		}
		printf("%.1f\n", grade); 
	}
	//找到错误最多的那个选项
	int maxcnt = 0;
	for(int i = 0;i < m;i++){
		for(int j = 0;j < 5;j++){
			maxcnt = cnt[i][j] > maxcnt ? cnt[i][j] : maxcnt;
		}
	} 
	if(maxcnt == 0)
		cout<<"Too simple"<<endl;
	else{
		for(int i = 0;i < m;i++){
			for(int j = 0;j < 5;j++){
				if(cnt[i][j] == maxcnt)
					printf("%d %d-%c\n",maxcnt,i+1,'a'+j);
			}
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值