题目传送门<==戳这
本题最重要的一个问题:如何分辨 全对,半对,错误 这三种答题情况。不妨这样想:令 a = 2^0 ,b = 2^1 ,c = 2^2, d=2^3 ,e=2^4
即如果选择的是a,那么该同学这道题的答案是1
如果选择的是 a ,c 那么该同学这道题的答案是 1+4=5
为什么要这么处理?位运算符 : ^ 异或运算符 (二进制位相同为0,不同为1)| 按位异或运算符 (二进制位有1则为1,都是0则为0)& 按位与运算符 (二进制位有0则为0,都为1才是1)那么如果正确答案是 a,c 而同学选择的是 a,b。
即正确答案是:5 ,同学的答案是3
(转化为二进制)即:101 和 011 , 那么5^1 ==110(二进制),记为sign=5^1那么我们就可以知道,如果异或运算之后,某一位是0,说明该选项正确,而如果是1,说明该选项错误或者没选
如何判断全对:我们将该题的正确选项按照上述方法转化后存入数组trueres[maxn]内
然后,将这道题的正确答案,和,该同学这道题选择的答案进行“异或运算”操作
如果该同学全对的话,操作后为0,否则说明没有全对
比如上面举的例子:正确答案:101,同学的答案:011 ,异或运算结果不为0,说明没有全对
如果部分正确:我们将正确答案和同学的答案进行“按位或”操作(|),
如果同学的答案没有选全,且没有选错,那么操作之后的结果还是等于正确答案。
比如这道题:正确答案:101,同学的答案:011,按位异或运算的结果为:111!=正确答案
说明同学选择的选项中有错误选项。
而如果同学只选了a选项,即答案为:001,那么按位异或运算的结果是101==正确答案
说明同学没有选全,并且没有错误选项
接下来,我们用数组 res [ 5 ] = {
1,2,4,8,16} 代表 a ~ e 。枚举k=0~5(即选项a ~ e)再对上述异或运算后的值sign进行“按位与”操作,即 & 操作
我们上面说过,sign中如果某一位有1,说明该选项错误或者没选,所以我们可以通过
按位与操作来判断sign中某一位是否为1
sign=110
k==0 res[k]=1(001) ,sign & res[k]==000 ==>a选项正确
k==1 res[k]=2(010) ,sign & res[k]==010 ==>b选项错误
k==2 res[k]=2(100) ,sign & res[k]==100 ==>c选项错误(没选也算错误)
解决掉上述关键问题,下面我们只需要记录下:每道题每个选项的错误次数,每个同学的得分即可
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int n,m,optnum,truenum,temp,maxcnt; //人数,题目数,题目总选项个数,正确答案选项个数,错误最大次数
int res[5]= {
1,2,4,8,16},ans[1005][105]; //res上面说过,ans记录第 i 位同学的第 j 题的答案
int main() {
char c;scanf("%d %d",&n,&m);vector<int> trueres(m+1),fullscore(m+1); //某题的正确答案,某题的满分vector<vector<int> > fal(m,vector<int>(5)); //嵌套vector的初始化,记录第 i 题的第 j 个选项的错误次数for(int i=0; i<m; i++) {
scanf("%d %d %d",&fullscore[i],&optnum,&truenum);for(int j=0; j<truenum; j++) {
scanf(" %c",&c);trueres[i]+=res[c-'a']; //记录正确答案}}for(int i=0; i<n; i++) {
double grade=0.0; //记录得分for(int j=0; j<m; j++) {
getchar(); //凡是遇到格式化输入都加上这条语句,防止出错scanf("(%d",&temp);for(int k=0; k<temp; k++) {
scanf(" %c)",&c);ans[i][j]+=res[c-'a']; //记录同学的答案}int sign=ans[i][j]^trueres[j]; //判断是否全对if(sign==0) {
//如果全对grade+=fullscore[j];} else {
if((ans[i][j] | trueres[j])==trueres[j]) {
//如果半对grade+=(1.0*fullscore[j]/2.0);}for(int k=0; k<5; k++) {
//注意半对也要记录错误选项,因为漏选的也算错误,只不过能得一半的分if(sign & res[k]) {
//如果该选项错误fal[j][k]++; //记录 j 题第 k 个选项的错误次数}}}}printf("%.1f\n",grade);}for(int i=0; i<m; i++) {
for(int j=0; j<fal[i].size(); j++) {
maxcnt=max(maxcnt,fal[i][j]); //记录错误次数的最大值}}if(maxcnt==0) cout<<"Too simple\n";else {
for(int i=0; i<m; i++) {
for(int j=0; j<fal[i].size(); j++) {
if(fal[i][j]==maxcnt) {
printf("%d %d-%c\n",maxcnt,i+1,'a'+j);}}}}
}