B1073 多选题常见计分法

题目链接:https://pintia.cn/problem-sets/994805260223102976/problems/994805263624683520

批改多选题是比较麻烦的事情,有很多不同的计分方法。有一种最常见的计分方法是:如果考生选择了部分正确选项,并且没有选择任何错误选项,则得到 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

最终AC代码如下:
#include <iostream>
#include <vector> 
using namespace std;

struct Problem{
    double score; //分数 
    int len1, len2;//总选项个数 正确答案个数
    vector<int> vi, re;//存选项  存错误选项 
    Problem(){
        score = 0;
        len1 = len2 = 0;
        vi.clear();
        re.clear();
    }
};

int main(){
    int i, j, n, m, t1, t2, maxn=0;
    double sum;
    char c;
    Problem p;
    vector<Problem> vp;
    cin>>n>>m;
    for(i=0; i<m; i++){
        p = Problem();
        cin>>p.score>>t1>>t2;
        p.len1 = t1;
        p.len2 = t2; 
        p.vi = vector<int>(t1, 0);
        p.re = vector<int>(t1, 0);
        for(j=0; j<t2; j++){
            cin>>c;
            p.vi[c-'a'] = 1;
        }
        vp.push_back(p);
    }
    while(n--){
        sum = 0;
        for(i=0; i<m; i++){
            c = 'a';
            while(c!='('){
                scanf("%c", &c);
            }
            scanf("%d", &t1);
            p = Problem();
            p.vi = vector<int>(vp[i].len1, 0);
            c = 'a';
            while(c!=')'){
                scanf("%c", &c);
                if(c>='a'){
                    p.vi[c-'a'] = 1;
                }
            }
            for(j=0; j<vp[i].len1; j++){
                if(p.vi[j]&(vp[i]).vi[j]){
                    p.len2++; //记录正确选项 
                }
                if(p.vi[j]^(vp[i]).vi[j]){
                    (vp[i]).re[j]++; //记录选项的错误记录
                    if((vp[i]).re[j]>maxn){
                        maxn = (vp[i]).re[j]; //存储所有选项的最大错误记录
                    }
                }
            }
            if(p.len2==t1){ //表示没有错选
                if(p.len2==vp[i].len2){ //全对 
                    sum += vp[i].score;
                }else{ //漏选 
                    sum +=  (0.5 * vp[i].score);
                }
            }
        }
        printf("%.1f
", sum);
    }
    if(maxn==0){ //表示没有选项被选错
        printf("Too simple
");
        return 0;
    }
    for(i=0; i<m; i++){
        for(j=0; j<vp[i].len1; j++){
            if((vp[i]).re[j] == maxn){
                printf("%d %d-%c
", maxn, i+1, 'a'+j);
            }
        }
    }
    return 0;
}

哈哈,可以说AC掉这道题,是刷所有PAT乙级题目中最开心的一道了,当然也是最后AC掉的。最让人不可置信的,我居然只提交一次就AC了。咳咳,有点得意忘形了,言归正传。

这种题目,看着很复杂,以及各种奇葩的条件让人瞬间没了自信。但是,其实却不一定是最难的,因为只要照着题干把所有要求实现了,或者更夸张地说,只要把下面的测试用例通过了,那么一般在OJ上的测试用例也能通过。简而言之,没有埋在地下的坑点。

主要问题有:一、如何判断全对、漏选以及错选三种情况。二、如何记录错误最多的选项,并且还要按序输出。

下面举一个小例子说明:

#include <iostream>
using namespace std;

int main(){
    int a[]={1,1,0,1}, b[]={1,0,1,0};//a[]为正确选项  b[]为学生选的选项
    for(int i=0; i<4; i++){
        //全对 a[i]&b[i]的结果中1的个数为k ,且k等于正确选项的个数
        //漏选 a[i]&b[i]的结果中1的个数为k,且k等于学生所选的个数
        cout<<"==&=="<<(a[i]&b[i])<<"....";
        //错选或漏选选项  a[i]^b[i]的结果中1的个数为k,有k个错误选项 
        cout<<"==^=="<<(a[i]^b[i])<<endl;
    }
    return 0;
}

对应的输出结果为:

==&==1....==^==0
==&==0....==^==1
==&==0....==^==1
==&==0....==^==1

注:(a[]、b[]中,下标0、1....代表a、b....)

从打印的结果可以知道,a选项选对了,a[]与b[]项与的结果为1的时候,就记录选对的次数k。如果k的值与学生选择的选择个数(暂记为cnt)不相等,那么该题为错选(如上例中,cnt=2,k=1)。在k==cnt的前提下,如果k的值与正确选项的个数(暂记为ans),那么该题全对(上例中的ans=3);否则,该题无错选但是漏选。

如此一来,第一个头疼的问题就被解决了。对于第二个问题,则看下面的分析。

b、d是漏选的项,c是选错的项,可以知道只要是a[]与b[]取非结果为1的时候,此时该选项就是被错选的,便在记录错选的变量+1,并且用一个maxn来记录最大的错选次数。其中,maxn初值为0,也就是说对完所有学生的答题情况后,若maxn==0,则所有学生都得满分;否则,按序遍历所有问题的所选选项,在满足某选项的错误次数k==maxn时,则输出此时的情况。由于是按序遍历,因为解决了并列情况的排序问题。

其中,较为重要的一点是,将每一个  问题 都当做一个对象来处理,具体写程序时就是定义一个结构体变量。如下:

struct Problem{
    double score; //分数 
    int len1, len2;//总选项个数 正确答案个数
    vector<int> vi, re;//存选项  存错误选项 
    Problem(){
        score = 0;
        len1 = len2 = 0;
        vi.clear();
        re.clear();
    }
};

哈哈,如此一来,所有问题都迎刃而解了,最后就是一些逻辑的判断了。

原文地址:https://www.cnblogs.com/heyour/p/12252951.html