zoukankan      html  css  js  c++  java
  • 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();
        }
    };

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

  • 相关阅读:
    poj2823单调队列认知
    有关二叉树的三序遍历的题目
    hdu4757 可持续字典树
    ZOJ2532判断边是否是割集中的边
    poj2455 k条路最小化最长边
    乘法逆元模板
    poj1699 KMP+壮压DP
    Innodb存储引擎——非聚集索引
    java集合框架笔记
    jvm垃圾回收
  • 原文地址:https://www.cnblogs.com/heyour/p/12252951.html
Copyright © 2011-2022 走看看