前言

算法课的第一次实验作业。在此浅作记录。

问题描述

随机点名程序(越不来上课的人,被点中的概率越高;实现抽查问题、预警等功能)。

问题分析

使用info.txt文件存放各学生旷课次数的信息,在程序运行时使用一个包含学生姓名、旷课次数和当堂课的出勤情况的结构体数组记录并更新相关信息。

随机抽取功能的实现则是使用checklist数组和rand函数:在初始化时对每个学生的序号记录进checklist数组中,其记录次数为2^(该学生的旷课次数),再用rand函数便能实现”越不来上课的人,被点中的概率越高“的功能。当某学生的旷课次数超过最大次数时,则取消其考试资格,且初始化checklist数组时不会将其序号计入。

点名及提问时用attendance(初始值均为0)记录该学生出勤情况(1表示出勤,-1表示旷课),使点名函数在随机抽到已抽过的学生时将再次抽取,提问函数在抽到已经确认出勤的学生时会直接提问。预警功能则是在学生未出勤时进行预警。

该程序还加入了查询函数,可列出各学生的旷课次数。

完整代码

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
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define MAXSIZE 210 //最大容量
#define MAX_ABSENT_TIME 5 //最大缺勤次数
struct stu_info {
char name[10]; //姓名
int abs_time; //已缺勤次数
int attendance; //当堂课的出勤情况
}stud[MAXSIZE];
int stu_num = 0; //学生总数
int checklist[20 * MAXSIZE] = { 0 }; //抽取的数组,其中checklist[0]用作总数量
int load()
{
FILE* fp;
if ((fp = fopen("info.txt", "rb")) == NULL) //打开文件
{
printf("无法打开文件!\n");
return 0;
}
while (!feof(fp))
{
fscanf(fp, "%s", stud[++stu_num].name); //读取姓名
fscanf(fp, "%d\n", &stud[stu_num].abs_time); //读取缺勤次数
stud[stu_num].attendance = 0; //每堂课前出勤情况初始化为0
}
printf("数据读取成功!共有%d名学生。\n", stu_num);
fclose(fp); //关闭文件
return 1;
}
void save()
{
int i;
FILE* fp;
if ((fp = fopen("info.txt", "wb")) == NULL) //打开文件
{
printf("无法打开文件!\n");
return;
}
for (i = 1; i <= stu_num; i++)
{
fprintf(fp, "%s\n%d\n", stud[i].name, stud[i].abs_time);
//将当前数据存入文件
}
printf("数据保存成功!即将退出......\n");
fclose(fp); //关闭文件
return;
}
void initchecklist()
{
int i, j;
for (i = 1; i <= stu_num; i++)
{
if (stud[i].abs_time != MAX_ABSENT_TIME) //未达到最大旷课次数时加入数组
for (j = 0; j < pow(2, stud[i].abs_time); j++) //加入次数为2^(该学生的旷课次数)次
checklist[++checklist[0]] = i;
}
}
void alert(int id)
{
stud[id].abs_time++;
if (stud[id].abs_time == MAX_ABSENT_TIME) //达到最大旷课次数
printf("%s同学已旷课%d次,达到最大旷课次数,取消考试资格!\n", stud[id].name, stud[id].abs_time);
else
printf("%s同学已旷课%d次,请注意!\n", stud[id].name, stud[id].abs_time);
return;
}
void check_att(int k)
{
int att;
printf("%s同学是否出勤?\n", stud[checklist[k]].name);
scanf("%d", &att); //是否出勤的参数,0表示旷课,其余为出勤
if (att)
stud[checklist[k]].attendance = 1; //出勤
else
{
stud[checklist[k]].attendance = -1; //旷课
alert(checklist[k]);
}
return;
}
void CheckName(int num)
{
int i = 1, k;
while (i <= num)
{
k = rand() % checklist[0] + 1; //生成随机数
if (!stud[checklist[k]].attendance) //未点名过
{
check_att(k);
i++;
}
}
return;
}
void AnswerQuestion(int num)
{
int i = 1, k, ans;
while (i <= num)
{
k = rand() % checklist[0] + 1; //生成随机数
if (!stud[checklist[k]].attendance) //未点名过
check_att(k);
if (stud[checklist[k]].attendance == 1) //该学生出勤则回答问题
{
printf("%s同学,请回答:", stud[checklist[k]].name);
scanf("%d", &ans); //问题正确与否的参数,0表示错误,其余为正确
if (ans)printf("回答正确!\n");
else printf("回答错误!\n");
i++;
}
}
return;
}
void find()
{
int i;
for (i = 1; i < stu_num; i++) //遍历
printf("%s同学已旷课%d次。\n", stud[i].name, stud[i].abs_time);
return;
}
int main()
{
srand((unsigned)time(0)); //以当前时间初始化种子,保证每次运行的随机数次序不同
int i, opt, n;
if (!load())return 0; //读取文件,文件不存在时退出程序
initchecklist(); //初始化checklist数组
printf("输入“1”以点名,输入“2”以抽查问题,输入“3”以查询旷课次数,输入“0”以保存并退出程序。\n");
while (1)
{
printf("请输入您要进行的操作:");
scanf("%d", &opt);
if (!opt)
{
save(); //保存文件
return 0; //退出
}
else if (opt == 1)
{
printf("输入点名人数:");
scanf("%d", &n);
CheckName(n); //点名
}
else if (opt == 2)
{
printf("输入提问人数:");
scanf("%d", &n);
AnswerQuestion(n); //提问
}
else if (opt == 3)
{
find(); //查询各学生的旷课次数
}
}
return 0;
}

后记

用到的基本上都是早已学过且用过多次的方法,写起来也没什么难度。与其说这是一个算法题目,不如说这就是一个超小型的项目。不过大项目也正是靠这些看起来很小的部分结合而成的,而且这也已经可以拿来在现实生活中运用了。

所以老师应该不会从大家的作业中拿一份出来用吧不会吧不会吧.jpg