乙级的题目训练主要用来熟悉编程语言的语法和形成良好的编码习惯和编码规范。从小白开始逐步掌握用编程解决问题。
PAT乙级BasicLevelPractice 1073 多选题常见计分法
问题分析
题设要求按照老师批改多选题的方法来计算学生的总分, 并且统计哪道题的选项选错的人最多。(选错的情况包括: 选了错误的和没选正确的两种)
多选题得分的规则为: 选择与答案一致得全部分数, 漏选得一半分数, 一旦选了正确选项之外的选项, 不得分。
本题的核心在于计算单道多选题学生得分的逻辑, 以及统计选项错误情况的逻辑;
由于要统计选项错误的情况, 所以不管能不能通过学生作答个数与正确选项个数之间关系直接得分情况, 都要挨个检查选项。
完整描述步骤
- 获取输入: 学生人数, 题目数量、
- 初始化统计器:
- 题目选项错误次数统计器
- 学生总分统计器
- 单个选项错误次数最大值 = 0
- 对于每一个学生:
- 读取每一道题的作答, 并计算分数:
- 初始化记录器:
- 作答与正确答案完全一致 = True
- 作答错误 = False
- 对于每一个选项:
如果是正确选项且学生没有选择这个选项:
- 设置 作答与正确答案完全一致 = False
- 该选项在 题目选项错误次数统计器 中 次数+1
- 如果选项错误次数 > 单个选项错误次数最大值:
- 单个选项错误次数最大值 = 当前选项错误次数
如果不是正确选项但是学生选择了这个选项:
- 设置 作答错误 = True
- 该选项在 题目选项错误次数统计器 中 次数+1
- 如果选项错误次数 > 单个选项错误次数最大值:
- 单个选项错误次数最大值 = 当前选项错误次数
- 如果 作答与正确答案完全一致 == True:
- 则学生总分加上该题分值
- 如果 作答错误 == True:
- 不加分
- 否则:
- 学生总分加上该题分值的一半
- 初始化记录器:
- 读取每一道题的作答, 并计算分数:
- 如果 单个选项错误次数最大值 == 0:
- (说明没有任何学生在任何选项上出错, 即全部学生全对)
- print(“Too simple”)
- 如果 单个选项错误次数最大值 != 0:
- 对于题目选项错误次数统计器中统计的各个选项以及错误次数:
- 如果 错误次数 等于 单个选项错误次数最大值:
- print(“{错误次数} {题目编号} {选项号}”)
- 如果 错误次数 等于 单个选项错误次数最大值:
- 对于题目选项错误次数统计器中统计的各个选项以及错误次数:
伪代码描述
- get input: student_amount, question_amount
- for each question:
- init recorder:
- option_wrong_counter = {0}
- answers = {0}
- option_max_amount = 0;
- get input: question’s score, answer_amount, correct answers
- init recorder:
- init recorder: student_scores = {0}
- for each student:
- for each question:
- get input: student’s answer_amount, answers
- init recorder:
- answer_wrong = False;
- answer_totally_right = True;
- for answer_selected_status in answers:
- (answer_selected_status = student’s answers[current_option])
- if question correct answers[current_option] == answer_selected_status:
- continue;
- answer_totally_right = False;
- question.option_wrong_counter[current_option] += 1;
- if question.option_wrong_counter[current_option] > option_max_amount:
- option_max_amount = question.option_wrong_counter[current_option];
- if question correct answers[current_option] == 0 and answer_selected_status == 1:
- answer_wrong = True;
- if answer_totally_right == True:
- student_scores[current student] += question.score;
- else if answer_wrong == False:
- student_scores[current student] += question.score / 2;
- for each question:
- if option_max_amount == 0:
- print(“Too simple”);
- if option_max_amount != 0:
- for each question:
- for each option:
- if question.option_wrong_counter[current_option] == option_max_amount:
- print(“{option_max_amount} {question_ID}-{current_option}”);
- if question.option_wrong_counter[current_option] == option_max_amount:
- for each option:
- for each question:
注意事项:
- 由于需要比较选项一致情况而且记录错误的具体选项, 而C语言中没有集合类型, 使用字符数组比较逻辑繁琐,
所以这里采用数组形式以0和1代表这个选项是否"是正确选项"以及是否"学生选择了这个选项", 只需要比较选项数组相同位置的值是否一致即可得到作答情况.
完整提交代码
/*
# 问题分析
题设要求按照老师批改多选题的方法来计算学生的总分, 并且统计哪道题的选项选错的人最多。(选错的情况包括: 选了错误的和没选正确的两种)
多选题得分的规则为: 选择与答案一致得全部分数, 漏选得一半分数, 一旦选了正确选项之外的选项, 不得分。
本题的核心在于计算单道多选题学生得分的逻辑, 以及统计选项错误情况的逻辑;
由于要统计选项错误的情况, 所以不管能不能通过学生作答个数与正确选项个数之间关系直接得分情况, 都要挨个检查选项。
# 完整描述步骤
1. 获取输入: 学生人数, 题目数量、
2. 初始化统计器:
- 题目选项错误次数统计器
- 学生总分统计器
- 单个选项错误次数最大值 = 0
3. 对于每一个学生:
- 读取每一道题的作答, 并计算分数:
- 初始化记录器:
- 作答与正确答案完全一致 = True
- 作答错误 = False
- 对于每一个选项:
- 如果是正确选项且学生没有选择这个选项:
- 设置 作答与正确答案完全一致 = False
- 该选项在 题目选项错误次数统计器 中 次数+1
- 如果选项错误次数 > 单个选项错误次数最大值:
- 单个选项错误次数最大值 = 当前选项错误次数
- 如果不是正确选项但是学生选择了这个选项:
- 设置 作答错误 = True
- 该选项在 题目选项错误次数统计器 中 次数+1
- 如果选项错误次数 > 单个选项错误次数最大值:
- 单个选项错误次数最大值 = 当前选项错误次数
- 如果 作答与正确答案完全一致 == True:
- 则学生总分加上该题分值
- 如果 作答错误 == True:
- 不加分
- 否则:
- 学生总分加上该题分值的一半
4. 如果 单个选项错误次数最大值 == 0:
- (说明没有任何学生在任何选项上出错, 即全部学生全对)
- print("Too simple")
5. 如果 单个选项错误次数最大值 != 0:
- 对于题目选项错误次数统计器中统计的各个选项以及错误次数:
- 如果 错误次数 等于 单个选项错误次数最大值:
- print("{错误次数} {题目编号} {选项号}")
# 伪代码描述
1. get input: student_amount, question_amount
2. for each question:
- init recorder:
- option_wrong_counter = {0}
- answers = {0}
- option_max_amount = 0;
- get input: question's score, answer_amount, correct answers
3. init recorder: student_scores = {0}
3. for each student:
- for each question:
- get input: student's answer_amount, answers
- init recorder:
- answer_wrong = False;
- answer_totally_right = True;
- for answer_selected_status in answers:
- (answer_selected_status = student's answers[current_option])
- if question correct answers[current_option] == answer_selected_status:
- continue;
- answer_totally_right = False;
- question.option_wrong_counter[current_option] += 1;
- if question.option_wrong_counter[current_option] > option_max_amount:
- option_max_amount = question.option_wrong_counter[current_option];
- if question correct answers[current_option] == 0 and answer_selected_status == 1:
- answer_wrong = True;
- if answer_totally_right == True:
- student_scores[current student] += question.score;
- else if answer_wrong == False:
- student_scores[current student] += question.score / 2;
4. if option_max_amount == 0:
- print("Too simple");
5. if option_max_amount != 0:
- for each question:
- for each option:
- if question.option_wrong_counter[current_option] == option_max_amount:
- print("{option_max_amount} {question_ID}-{current_option}");
# 注意事项:
1. 由于需要比较选项一致情况而且记录错误的具体选项, 而C语言中没有集合类型, 使用字符数组比较逻辑繁琐,
所以这里采用数组形式以0和1代表这个选项是否"是正确选项"以及是否"学生选择了这个选项", 只需要比较选项数组相同位置的值是否一致即可得到作答情况.
*/
# include<stdio.h>
typedef struct{
int score;
int option_amount;
int answer_amount;
int answers[5]; // 为了比较答案和记录错误选项方便, 用0和1记录选项1~5是否属于正确选项
int option_wrong_counter[5];
} question;
int option_max_amount = 0;
double check_answer_and_return_score(question current_question, int* answers, int* option_wrong_counter){
int answer_wrong = 0;
int answer_totally_right = 1;
for(int i = 0; i < 5; i++){
if (current_question.answers[i] == answers[i]){
continue;
}
answer_totally_right = 0;
option_wrong_counter[i]++;
if (option_wrong_counter[i] > option_max_amount){
option_max_amount = option_wrong_counter[i];
}
if (current_question.answers[i] == 0 && answers[i] == 1){
answer_wrong = 1;
}
}
if (answer_wrong == 1) return 0;
if (answer_totally_right == 1) return current_question.score;
return current_question.score / 2.0;
}
int main(){
int student_amount, question_amount;
scanf("%d %d", &student_amount, &question_amount);
question questions[question_amount];
for (int i = 0; i < question_amount; i++){
memset(questions[i].answers, 0, 5 * sizeof(int));
memset(questions[i].option_wrong_counter, 0, 5 * sizeof(int));
scanf("%d %d %d", &questions[i].score, &questions[i].option_amount, &questions[i].answer_amount);
char answer;
for(int j = 0; j < questions[i].answer_amount; j++){
scanf(" %c", &answer);
questions[i].answers[(answer - 'a')] = 1;
}
}
double student_scores[student_amount];
memset(student_scores, 0, student_amount * sizeof(double));
for (int i = 0; i < student_amount; i++){
for (int j = 0; j < question_amount; j++){
int answer_amount;
char answer;
int answers[5] = {0};
getchar(); // 读取换行或空格
getchar(); // 读取左括号
scanf("%d", &answer_amount);
for (int k = 0; k < answer_amount; k++){
scanf(" %c", &answer);
answers[(answer - 'a')] = 1;
}
getchar(); // 读取右括号
student_scores[i] += check_answer_and_return_score(questions[j], answers, questions[j].option_wrong_counter);
}
}
for (int i = 0; i < student_amount; i++){
printf("%.1f\n", student_scores[i]);
}
if (option_max_amount == 0){
printf("Too simple\n");
return 0;
}
for (int i = 0; i < question_amount; i++){
for (int j = 0; j < 5; j++){
if (questions[i].option_wrong_counter[j] == option_max_amount){
printf("%d %d-%c\n", option_max_amount, i+1, 'a' + j);
}
}
}
return 0;
}