【PAT乙级】1073 多选题常见计分法

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

个人思路

这题思路上比较简单,但是实现起来有些复杂。我用了两个结构体,第一个用来存储每个单选题的信息,第二个用来存储错误的选项信息。然后有点麻烦的就是对输入的字符串进行处理。

有一个小坑点要注意,就是题目中要统计的错误选项有两种

1、选错的项

2、漏选的项

注意然后就是写代码完成就好啦。

代码实现

#include <cstdio>
#include <cstring>
#include <string>
#include <set>
#include <cmath>
#include <algorithm>
#include <iostream>
#define ll long long
#define ep 1e-5
#define INF 0x7FFFFFFF

const int maxm = 105;

using namespace std;

// 记录每道多选题的信息
struct Question {
    int score, item_num, ans_num;
    bool ans_rec[30];
    char items[30];
};
Question questions[maxm];

// 记录每个错误选项的信息
struct Wrong {
    int question_id, num;
    char wrong_item;
};
Wrong wrong_rec[maxm*5];
int wrong_cnt = 0;

// 添加错误记录
void add_wrong_rec(int id, char item) {
    // 如果这道题这个选项之前错过,计数+1
    bool wrong_before = false;
    for (int k = 0; k < wrong_cnt; k ++) {
        if (wrong_rec[k].question_id == id && wrong_rec[k].wrong_item == item) {
            wrong_rec[k].num ++;
            wrong_before = true;
            break;
        }
    }
    // 如果是第一次错,添加错误记录
    if (!wrong_before) {
        wrong_rec[wrong_cnt].question_id = id;
        wrong_rec[wrong_cnt].num ++;
        wrong_rec[wrong_cnt].wrong_item = item;
        wrong_cnt ++;
    }
}

// 将错误记录按错误个数从大到小排序,如果相同,按题目编号从小到大排序,如果又相同,按照错误选项从小到大排序
bool cmp(const Wrong w1, const Wrong w2) {
    if (w1.num == w2.num) {
        if (w1.question_id == w2.question_id)
            return w1.wrong_item < w2.wrong_item;
        return w1.question_id < w2.question_id;
    }
    return w1.num > w2.num;
}

int main() {
    // 初始化
    int n, m;
    memset(questions, 0, sizeof(questions));
    memset(wrong_rec, 0, sizeof(wrong_rec));
    
    // 输入
    cin >> n >> m;
    for (int i = 1; i <= m; i ++) {
        cin >> questions[i].score >> questions[i].item_num >> questions[i].ans_num;
        for (int j = 0; j < questions[i].ans_num; j ++) {
            char ans_item;
            cin >> ans_item;
            questions[i].items[j] = ans_item;
            questions[i].ans_rec[ans_item-'a'] = true;
        }
    }
    getchar();
    
    // 对n个学生进行批改
    while (n --) {
        // 初始化每个学生的分数为0
        double stu_score = 0.0;
        int begin = 0, end = 0;
        string stu_ans;
        getline(cin, stu_ans);
        
        // 对m道题进行批改
        for (int i = 1; i <= m; i ++) {
            bool is_wrong = false;
            for (int j = end+1; j < stu_ans.length(); j ++) {
                if (stu_ans[j] == ')') {
                    end = j;
                    break;
                }
            }
            int stu_ans_num = stu_ans[begin+1]-'0';
            bool tmp_rec[30];
            memset(tmp_rec, 0, sizeof(tmp_rec));
            
            // 对每个选项进行检查
            for (int j = begin+3; j <= end-1; j ++) {
                if (stu_ans[j] != ' ') {
                    // 记录学生选过的
                    tmp_rec[stu_ans[j]-'a'] = true;
                    // 如果选项错误,记录错误记录
                    if (!questions[i].ans_rec[stu_ans[j]-'a']) {
                        is_wrong = true;
                        add_wrong_rec(i, stu_ans[j]);
                    }
                }
            }
            
            //对未选项进行记录
            for (int j = 0; j < questions[i].ans_num; j ++) {
                if (!tmp_rec[questions[i].items[j]-'a']) {
                    add_wrong_rec(i, questions[i].items[j]);
                }
            }
            
            // 如果没有错误选项
            if (!is_wrong && stu_ans_num > 0){
                double add_score = 0.0;
                // 全对
                if (stu_ans_num == questions[i].ans_num) {
                    add_score = questions[i].score;
                }
                // 部分对
                else {
                    add_score = 1.0*questions[i].score/2;
                }
                stu_score += add_score;
            }
            begin = end + 2;
        }
        printf("%.1lf\n", stu_score);
    }
    
    // 输出
    if (wrong_cnt == 0) {
        cout << "Too simple" << endl;
    }
    else {
        sort(wrong_rec, wrong_rec+wrong_cnt, cmp);
        int max_wrong = wrong_rec[0].num, i = 0;
        while (wrong_rec[i].num == max_wrong) {
            cout << wrong_rec[i].num << " " << wrong_rec[i].question_id << "-" << wrong_rec[i].wrong_item << endl;
            i ++;
        }
    }
    
    return 0;
}

总结

学习不息,继续加油

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值