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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
#include <limits.h>

#define ANSWER_SIZE 110

typedef struct {
char option; //选项
int count; //错误次数
} MistakeOption;

typedef struct {
int score; //满分
int count; //选项个数
int correct; //正确选项个数
char options[10]; //答案选项
MistakeOption mistakeOptions[10]; //选错选项
int mistakeSize; //选错选项个数
int maxMistakeCount; //最大选错次数
} Answer;

typedef struct {
int num; //题目编号
char mistakeOptions[10]; //错的最多的选项
} Result;

int cmp(const void *left, const void *right)
{ return ((Result *)left)->num - ((Result *)right)->num; }

int cmp2(const void *left, const void *right)
{ return *(char *)left - *(char *)right; }

//字符转整数
int ctoi(char c)
{ return c - '0'; }

//读取选项,并存入option
void readChoice(char *options, int size)
{
int c;
char *p = options;

while ((c = getchar()) != EOF && c != '\n' && p - options < size) {
//忽略空白
while (isspace(c)) {
c = getchar();
}
*p++ = c;
}
*p = '\0';
}

//输入答案
void inputAnswer(Answer *answer)
{
scanf("%d%d%d", &answer->score, &answer->count, &answer->correct);
readChoice(answer->options, 10);
answer->mistakeSize = 0;
answer->maxMistakeCount = 0;
}

//将错误选项mistakeOption加入到answer中
void pushMistake(Answer *answer, char mistakeOption)
{
bool existed = false;

//若已存在,增加其计数器
for (int i = 0; i < answer->mistakeSize; ++i) {
if (answer->mistakeOptions[i].option == mistakeOption) {
if (answer->maxMistakeCount < ++answer->mistakeOptions[i].count) {
answer->maxMistakeCount = answer->mistakeOptions[i].count;
}
existed = true;
break;
}
}
//不存在则添加到其中
if (!existed) {
answer->mistakeOptions[answer->mistakeSize].option = mistakeOption;
answer->mistakeOptions[answer->mistakeSize++].count = 1;
if (answer->maxMistakeCount < 1) {
answer->maxMistakeCount = 1;
}
}
}

//找出错误的选项并添加到answer,若全错则返回TRUE,否则返回false
//全错:即options中包含错误选项
bool findMistake(Answer *answer, char *options)
{
int index = 0;
bool mistake = false;

while (options[index]) {
int i = 0;

while (i < answer->correct && answer->options[i] != options[index]) {
++i;
}
if (i == answer->correct) {
pushMistake(answer, options[index]);
mistake = true;
}
++index;
}

for (int i = 0; i < answer->correct; ++i) {
int cur = 0;

while (options[cur] && answer->options[i] != options[cur]) {
++cur;
}
if (!options[cur]) {
pushMistake(answer, answer->options[i]);
}
}

return mistake;
}

//从answer中找出错误次数最多的选项,存入result中
void getMaxMistakeOptions(const Answer *answer, char *result)
{
for (int i = 0; i < answer->mistakeSize; ++i) {
if (answer->mistakeOptions[i].count == answer->maxMistakeCount) {
*result++ = answer->mistakeOptions[i].option;
}
}
*result = '\0';
}

//读取学生的一题选项答案,存入option
void readStudentChoice(char *options, int size)
{
int c;
char *p = options;

while ((c = getchar()) != EOF && c != '(') {
}

while ((c = getchar()) != EOF && c != ')' && p - options < size) {
//忽略空白
while (isspace(c)) {
c = getchar();
}
*p++ = c;
}
*p = '\0';
}

int main(void)
{
int n, m;
Answer answer[ANSWER_SIZE];
Result result[ANSWER_SIZE];
int resultSize = 0;
int max = INT_MIN; //保存错误最多的次数
char s[100]; //仅用于读取scanf后残余的空白

scanf("%d%d", &n, &m);
fgets(s, 100, stdin);

for (int i = 0; i < m; ++i) {
inputAnswer(answer + i);
}
for (int i = 0; i < n; ++i) {
double grade = 0;

for (int j = 0; j < m; ++j) {
char options[40]; //学生选的选项

readStudentChoice(options, 40);
if (!findMistake(answer + j, options + 1)) {
//ctoi(options[0]) == answer[j].correct说明全对
grade += answer[j].score / (ctoi(options[0]) == answer[j].correct ? 1.0 : 2.0);
}
}
printf("%.1f\n", grade);
}

//找出错误最多的次数
for (int i = 0; i < m; ++i) {
if (answer[i].maxMistakeCount > max) {
max = answer[i].maxMistakeCount;
}
}

//将所有错误次数为max的题的信息存入result
for (int i = 0; i < m; ++i) {
if (max && answer[i].maxMistakeCount == max) {
result[resultSize].num = i + 1;
getMaxMistakeOptions(answer + i, result[resultSize].mistakeOptions);
++resultSize;
}
}

//按编号从小到大排序
qsort(result, resultSize, sizeof(Result), cmp);

for (int i = 0; i < resultSize; ++i) {
char *p = result[i].mistakeOptions;

//按字母从小到大排序
qsort(p, strlen(p), sizeof(char), cmp2);
while (*p) {
printf("%d %d-%c\n", max, result[i].num, *p);
++p;
}
}

if (resultSize == 0) {
printf("Too simple\n");
}

return 0;
}
Author: sphc
Link: https://jkuvw.xyz/archives/4e627875/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
微信打赏
支付宝打赏