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();
        }
    };

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

  • 相关阅读:
    算法竞赛入门经典习题2-3 韩信点兵
    ios入门之c语言篇——基本函数——5——素数判断
    ios入门之c语言篇——基本函数——4——数值交换函数
    144. Binary Tree Preorder Traversal
    143. Reorder List
    142. Linked List Cycle II
    139. Word Break
    138. Copy List with Random Pointer
    137. Single Number II
    135. Candy
  • 原文地址:https://www.cnblogs.com/heyour/p/12252951.html
Copyright © 2011-2022 走看看