看前须知
第六次上机题汇总
单词查找+查找算法Hash和Trie的拓展(可能和大作业有关).
题目内容
问题描述
【问题描述】某班级要进行期末考试,准备考试时打乱座次,现已按照学号顺序人工为学生随机安排了座位号,但其中可能会出现漏排和重复安排座位的情况。编写程序读入人工安排的考试座位安排表T1,对安排情况进行检查,并对漏排和重复安排座位的情况进行修正,修正后,若学生人数为N,则每位学生考试座位安排应在1~N之间,其中没有缺号和重号。假设T1中学号信息不会出现重复,同一座位号最多有两位同学的座位号相同,并且座位号不会连续漏排;初始考试座位安排表存放在当前目录下的in.txt中,其中包括每位学生的学号、姓名和座位号,要求修正后的考试座位安排表输出到当前目录下的out.txt文件中。程序检查座位号的规则如下:
-
首先对考试座位安排表T1按座位号从小到大的顺序排序(原始考试安排可能有座位号相同情况,座位号相同时则按原始学号顺序排序),得到按座位号排序的安排表T2;
-
对表T2从头开始检查漏排座位号情况:假设当前表中安排的最大座位号为M,取M和N的较小值Q;从1号开始检查,若某个小于等于Q的座位序号没有安排学生,则将表T2的最后学生的座位设置为该座位号;若存在多个漏排座位,则从表T2最后依次向前设置;
-
然后再检查表T2中重排座位号情况:假设当前表中安排的最大座位号为m,将座位号重复的、学号较大的学生的座位号依次设置为m+1、m+2、m+3…;
-
将调整好的表T2按学号由小到大序排序后按输出格式要求输出至指定输出文件中。
输入形式
从标准输入中读入学生人数(不超过100的正整数)。
初始考试座位安排表存储在当前目录下的in.txt文件中,已按照学号由小到大的顺序分行存储每位学生座位信息,依次为学生学号(不超过8位的正整数)、姓名(由不超过20位的英文字母组成)和座位号(不超过100的正整数),各数据间以一个空格分隔。最后一个学生座位信息后有回车换行。
输出形式
按照学号由小到大的顺序将修正后的考试座位安排表输出到当前目录下的out.txt文件中,每行依次为学号、姓名和座位号,各数据之间以一个空格分隔。
样例
24
假设当前目录下的in.txt文件内容如下:
18373001 ShiTian 7
18373002 WangLi 15
18373003 LiGuoHong 23
18373005 QianSanQiang 26
18373006 ZhangQiang 8
18373007 SunXiXi 2
18373010 LiXing 12
18373011 TangYing 20
18373012 YangYang 4
18373013 ZhaoGang 27
18373014 ZhouLiang 18
18373015 WuShuo 9
18373016 ZhengSiSi 13
18373017 WangGong 27
18373018 TianTian 21
18373020 LeiLei 16
18373021 ZhangLou 10
18373022 WangLei 17
18373025 SunTian 24
18373026 JinXiang 18
18373028 PangHong 11
18373029 GaoLiang 2
18373030 GaoHang 6
18373031 YangYang 22
【样例输出】
当前目录下的out.txt文件内容应为:
18373001 ShiTian 7
18373002 WangLi 15
18373003 LiGuoHong 19
18373005 QianSanQiang 5
18373006 ZhangQiang 8
18373007 SunXiXi 2
18373010 LiXing 12
18373011 TangYing 20
18373012 YangYang 4
18373013 ZhaoGang 3
18373014 ZhouLiang 18
18373015 WuShuo 9
18373016 ZhengSiSi 13
18373017 WangGong 1
18373018 TianTian 21
18373020 LeiLei 16
18373021 ZhangLou 10
18373022 WangLei 17
18373025 SunTian 14
18373026 JinXiang 24
18373028 PangHong 11
18373029 GaoLiang 23
18373030 GaoHang 6
18373031 YangYang 22
样例说明
初始考试座位安排表中有24位学生的排位信息,正确情况下这些学生安排的座位号应为1~24。初始人工安排的座位信息有1、3、5、14和19号座位漏排,有2、18和27号座位重复安排学生。先对漏排的座位进行修正:已安排的最大座位号为27,学号为18373017和18373013的学生分别安排了27号座位,按照漏排座位修正规则,先将18373017学生安排在1号座位,再将18373013学生安排在3号座位;同理分别将18373005学生安排在5号座位,将18373025学生安排在14号座位,18373003号学生安排在19号座位。当前安排的最大座位号为22,还有2号和18号座位重复,将2号重复的学号较大的18373029号学生安排在23号座位,将18号重复的学号较大的18373026号学生安排在24号座位。这样修正后按照学号由小到大的顺序输出学生座位信息到out.txt中。
题解
思考和详解
这道题十分有技巧,尤其是第二步和第三步操作。
第二步,如果我们采用朴素的两个for嵌套来寻找缺号的话,其实是有一定难度的,时间复杂度先不说,光怎么判断循环结尾都有点困难。其次,题目所说的Q,是实时变化的,因为补号的时候可能会让当前最大的号码或人数中的最小值改变,因此采用朴素的查找是十分困难的。所以再排完序之后我们只需要相邻两个编号比较就行(相减大于1说明缺号,直接补就OK,而且复杂度降了一级)
第三步,同理,如果用朴素的两个for嵌套来寻找重号可以实现,但是可能会有一点小问题(第五个测试点过不了),所以我们还是采用之前的办法——相邻两个编号比较就行(相减等于0说明重号,此时直接补充就ok了)
参考代码
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<ctype.h>
#include<stdbool.h>
struct seat{ //座位结构体
char ID[200]; //学号
char name[200]; //姓名
int seatID; //座位号
int when; //原始次序
};
struct seat s[200];
int i,j,StuNum,maxseatID,k,n;
int cmp(const void*p1,const void*p2);
int cmp2(const void*p1,const void*p2);
int main()
{
i=1,k=1;
FILE *fp = fopen("in.txt", "r");
FILE *fp2 = fopen("out.txt", "w");
scanf("%d",&n);
for(i=1;i<=n;i++)
{
fscanf(fp, "%s %s %d",s[i].ID,s[i].name,&s[i].seatID);
s[i].when=i; //记录原始次序
}
StuNum=i; //记录人数(为什么不用 n ,是因为我是 i = 1 开始记录的,所以快排和输出时同 n 不方便, 其次是用 n 可能会有一些小问题)
qsort(s,StuNum,sizeof(struct seat),cmp);
maxseatID=StuNum-1 < s[StuNum-1].seatID ? StuNum-1 : s[StuNum-1].seatID; //得到较小的标号
k=n;
for ( i = 1; i <= maxseatID; i++)
{ //十分有技巧,如果仅仅是单个去查找的算法(for 套 for )时间复杂度会很大而且最终如何判断循环结束会有很大问题
if (i == 1 && s[1].seatID > 1)
{
s[k].seatID = 1; //直接设为1
k--;
continue;
}
if (s[i].seatID - s[i-1].seatID > 1) //如果之间漏号。直接补充
{
s[k].seatID = s[i-1].seatID + 1;
k--;
}
}
qsort(s,StuNum,sizeof(struct seat),cmp);
k=maxseatID=s[StuNum-1].seatID;
for(i=2;i<=n;i++) //注意是从2开始的
{
if (s[i].seatID - s[i-1].seatID == 0) //如果重号,直接赋予最新编号
{
k++;
s[i].seatID = k;
}
}
qsort(s,StuNum,sizeof(struct seat),cmp2);
for(i=1;i<StuNum;i++)
fprintf(fp2,"%s %s %d\n",s[i].ID,s[i].name,s[i].seatID);
//printf("%s %s %d\n",s[i].ID,s[i].name,s[i].seatID);
return 0;
}
int cmp(const void*p1,const void*p2)
{
struct seat *a=(struct seat*)p1;
struct seat *b=(struct seat*)p2;
if(a->seatID!=b->seatID) return a->seatID-b->seatID;
else return a->when-b->when;
}
int cmp2(const void*p1,const void*p2)
{
struct seat *a=(struct seat*)p1;
struct seat *b=(struct seat*)p2;
return strcmp(a->ID,b->ID);
}
补充测试的数据
假设当前目录下的in.txt文件内容如下:
18876001 lixing 3
18876002 wanglili 6
18876005 sunqi 2
18876006 langwen 5
18876007 zhujun 11
18876009 caiming 15
18876010 jiangxun 1
18876011 huanglei 7
18876012 yujiajia 9
18876013 tianpei 3
18876014 huanglian 19
18876015 lansimiao 4
18876016 tandelai 7
18876017 lishuhao 5
18876018 yaoming 17
18876019 sunlili 2
18876020 yangjinian 6
18876022 qiushaoyun 13
18876023 lishutong 4
18876025 zhaolei 1
【样例输出】
当前目录下的out.txt文件内容应为:
18876001 lixing 3
18876002 wanglili 6
18876005 sunqi 2
18876006 langwen 5
18876007 zhujun 11
18876009 caiming 12
18876010 jiangxun 1
18876011 huanglei 7
18876012 yujiajia 9
18876013 tianpei 16
18876014 huanglian 8
18876015 lansimiao 4
18876016 tandelai 20
18876017 lishuhao 18
18876018 yaoming 10
18876019 sunlili 15
18876020 yangjinian 19
18876022 qiushaoyun 13
18876023 lishutong 17
18876025 zhaolei 14