PAT 乙级练习 题解合集
题目
批改多选题是比较麻烦的事情,有很多不同的计分方法。有一种最常见的计分方法是:如果考生选择了部分正确选项,并且没有选择任何错误选项,则得到 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. 处理M
道题的输入
problems
记录M
个题目信息(见下方代码),其中point
表示该题分值,ans
的设定有一点复杂,允许我举例说明:
例如,对于输入3 4 2 a c
,point
的值是3
,ans
是"aBcD"
,字符串长度为 4 表示总共有 4 个选项,其中小写的两个字母代表正确选项。
又例如,输入1 5 4 a b d e
,ans
应该是abCde
。
struct Problem {
double point;
string ans;
};
vector<Problem> problems(m);
2. 输入答卷的同时给出成绩,对错误选项计数
vector<vector<int> > wrongCnt(m, vector<int>(5, 0));
定义wrongCnt
对错误选项计数;- 对于第
i
名学生,用sum
来统计其总分,每一轮开始时要初始化为 0; - 对于第
i
名学生答的第j
道题,按照以上提及的答案表示方法生成对应的字符串ans
,例如一个题总共有 5 个选项,学生选了a
,b
,那么生成abCDE
; - 如果
ans == problems[j].ans
,那么完全正确,为sum
增加该题的分值; - 如果
ans != problems[j].ans
,需要用k
遍历两个字符串来比较其区别
a. 只要遇到ans[k] != problems[j].ans[k]
,说明这个选项错了,wrongCnt
中对应的选项要计数 1 次;
b. 还要判断是什么样的错误,如果是多选的情况,那么该生这一题全错;
遍历结束后,如果该生没有多选,说明是漏选,要为sum
加上这个题一半的分值。 - 批完第
i
名学生的考卷后立即输出其sum
;
3. 找到最大的错误次数,输出这些选项
- 遍历
wrongCnt
,找到最大的错误次数maxWrongCnt
; - 如果
maxWrongCnt == 0
,输出Too simple
; - 否则,再次遍历
wrongCnt
,输出所有错得最多的选项。
代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <string>
#include <cctype>
using namespace std;
const string ABCDE = "ABCDE";
struct Problem {
double point;
string ans;
};
int main() {
int n, m, len, correctCnt;
string input;
cin >> n >> m;
vector<Problem> problems(m);
vector<vector<int> > wrongCnt(m, vector<int>(5, 0));
for (int i = 0; i < m; ++i) {
cin >> problems[i].point >> len >> correctCnt;
problems[i].ans = ABCDE.substr(0, len);
while (correctCnt--) {
cin >> input; // 小写代表正确选项
int temp = ABCDE.find(toupper(input[0]));
problems[i].ans[temp] = tolower(problems[i].ans[temp]);
wrongCnt[i][temp] = 0;
}
}
getline(cin, input); // 吃回车
for (int i = 0; i < n; ++i) { // 每个学生
double sum = 0.0;
getline(cin, input);
int p = 0;
for (int j = 0; j < m; ++j) { // 每道题
bool wrong = false;
while (input[p] != '(')
++p;
int cnt = input[++p] - '0';
string ans = ABCDE.substr(0, problems[j].ans.size());
for (int k = 0; k < cnt; ++k) { // 读入答题卡选项
p += 2;
int temp = ABCDE.find(toupper(input[p]));
ans[temp] = tolower(ans[temp]);
}
if (ans == problems[j].ans)
sum += problems[j].point;
else {
for (int k = 0; k < ans.size(); ++k) { // 对比每个选项
if (ans[k] != problems[j].ans[k]) {
++wrongCnt[j][k];
if (ans[k] > problems[j].ans[k])
wrong = true;
}
}
if (!wrong)
sum += 0.5 * problems[j].point;
}
}
printf("%.1lf\n", sum);
}
int maxWrongCnt = 0;
for (int i = 0; i < m; ++i)
for (int j = 0; j < 5; ++j)
if (wrongCnt[i][j] > maxWrongCnt)
maxWrongCnt = wrongCnt[i][j];
if (maxWrongCnt == 0)
puts("Too simple");
else
for (int i = 0; i < m; ++i)
for (int j = 0; j < 5; ++j)
if (wrongCnt[i][j] == maxWrongCnt)
printf("%d %d-%c\n", maxWrongCnt, i + 1, tolower(ABCDE[j]));
return 0;
}