BUAA数据结构期末考试(20级)

BUAA数据结构期末考试(20级)

1. 机试异常检测(简)

题目

问题描述

某教学平台具有考试登录异常检测功能,检测规则如下:

  1. 考试开始后,如果同⼀账号在不同机器上登录,系统将报警输出异常登录信息(可能存在私⾃换机器的情况);
  2. 考试开始后,如果同⼀账号在同⼀机器上多次登录,属于正常情况,系统不报警。

编写程序,读⼊某次考试学⽣的登录⽇志信息,对其进⾏异常检测,输出异常登录信息。⽇志信息包括学⽣学号(即学⽣账号,唯⼀ 标识学⽣身份的信息,⽤⼀整数表示,不超过int的表示范围)、学⽣姓名(⽤不含空⽩符的字符串表示,字符个数不超过15)、机器 号(⽤⼀整数表示,不超过int的表示范围)、登录时间(⽤包含6个数字的字符串表示,例如:093756,表示9点37分56秒)。

输入形式

先从控制台输⼊⽇志信息条数(不超过200条),然后按照登录先后顺序分⾏输⼊⽇志信息,每条信息包括学号、姓名、机器号和登 录时间,以⼀个空格分隔各数据。

输出形式

按照学号从⼩到⼤的顺序输出登录异常账号信息(仅包括学号和姓名),每条信息独占⼀⾏,学号和姓名以⼀个空格分隔。如果没有 异常登录信息,则什么都不输出。

样例输入
21
191028 wangdi 15 093000
192387 litong 39 093000
190877 liugang 37 093001
197583 huangqinian 196 093004
195211 liuhao 201 093005
193098 zhaogang 377 093006
190001 zhousheng 1 093007
190009 wuhong 12 093007
197583 huangqinian 197 093008
195877 lisisi 202 093008
192387 litong 309 093009
191000 tonghao 201 093402
197583 huangqinian 196 093500
191028 wangdi 15 093507
190010 wangzhuang 85 093558
195333 zhangye 63 093600
197583 huangqinian 195 094100
195211 liuhao 200 095103
190010 wangzhuang 287 095509
193098 zhaogang 377 095606
191028 wangdi 15 095709
样例输出
190010 wangzhuang
192387 litong
195211 liuhao
197583 huangqinian
样例说明

输⼊了21条登录⽇志信息,其中有四位学⽣(学号分别为190010、192387、195211和197583)在多台机器上登录,属于异常登录, 输出异常账号登录信息;注意:学号为191028的学⽣在15号机器上有多次登录,属于正常重复登录。

评分标准

该题要求辨别输出异常登录信息,提交程序名为login.c。

问题分析

本题肯定是考察排序,虽然没给我们排序规则,但其实一眼就能看出来这是个二级排序,理由如下:

  1. 第一级肯定要对学号进行排序,因为我们检测的是每个人有没有登录异常;
  2. 而针对每个人,其实我们要检测的就是在他的所有登录信息中,出没出现过两个一样的机器号,所以第二级排序是以机器号为关键字的排序。

具体处理过程

首先声明日志结构类型,并创建日志集合:

typedef struct {
    char indentifier[20];  // 学号
    char name[20];  // 姓名
    int number;  // 机器号
    char time[20];  // 登录时间
} logging;


logging gather[500];  // 日志集合

主函数中读入数据:

// 主函数中
    int n;  // 日志条数
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%s %s %d %s", gather[i].indentifier, gather[i].name, &gather[i].number, gather[i].time);
    }

而后要对数据排序,按照我们分析出来的规则设计cmp函数:

int cmp(const void *e1, const void *e2)
{
    logging *a = (logging*) e1;
    logging *b = (logging*) e2;
    if (strcmp(a -> indentifier, b -> indentifier) != 0) {
        return strcmp(a -> indentifier, b -> indentifier);
    }
    return a -> number - b -> number;
}

主函数中排序:

// 主函数中
    qsort(gather, n, sizeof(logging), cmp);

然后我们要检测每个学生是否由异常登录的情况。此时要设置两个标志变量:一个记录当前(或者说是上一个)检测的学生的学号信息,一个去记录我们上一次打印的异常的学生是谁(因为可能会有一个学生好几次异常的情况,此时只要打印一次)。我们分别在遍历到下一个学生和打印新的异常学生信息时更新这两个标志的值,代码如下:

// 主函数中
    char *now_id = gather[0].indentifier;
    char *last_print = "1";  // 随便初始化一个不可能的值
    for (int i = 1; i < n; i++) {
        if (strcmp(gather[i].indentifier, now_id) == 0) {  // 和上一个是一个人
            if (gather[i].number != gather[i - 1].number) {  // 同一个人登路的机器号不一样
                if (strcmp(gather[i].indentifier, last_print) != 0) {  // 和上一次打印的不是同一个人
                    printf("%s %s\n", gather[i].indentifier, gather[i].name);
                    last_print = gather[i].indentifier;
                } 
            }
         } else {
            now_id = gather[i].indentifier;
         }
    }

完整代码:

# include <stdio.h>
# include <stdlib.h>
# include <string.h>

typedef struct {
    char indentifier[20];  // 学号
    char name[20];  // 姓名
    int number;  // 机器号
    char time[20];  // 登录时间
} logging;


logging gather[500];  // 日志集合

int cmp(const void *e1, const void *e2);

int main()
{
    int n;  // 日志条数
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%s %s %d %s", gather[i].indentifier, gather[i].name, &gather[i].number, gather[i].time);
    }


    qsort(gather, n, sizeof(logging), cmp);

    char *now_id = gather[0].indentifier;
    char *last_print = "1";  // 随便初始化一个不可能的值
    for (int i = 1; i < n; i++) {
        if (strcmp(gather[i].indentifier, now_id) == 0) {  // 和上一个是一个人
            if (gather[i].number != gather[i - 1].number) {  // 同一个人登路的机器号不一样
                if (strcmp(gather[i].indentifier, last_print) != 0) {  // 和上一次打印的不是同一个人
                    printf("%s %s\n", gather[i].indentifier, gather[i].name);
                    last_print = gather[i].indentifier;
                } 
            }
         } else {
            now_id = gather[i].indentifier;
         }
    }


    return  0;
}


int cmp(const void *e1, const void *e2)
{
    logging *a = (logging*) e1;
    logging *b = (logging*) e2;
    if (strcmp(a -> indentifier, b -> indentifier) != 0) {
        return strcmp(a -> indentifier, b -> indentifier);
    }
    return a -> number - b -> number;
}

2. 函数调用关系

这题是我们这届的作业题,见这篇文章

3. 服务优化

本题也是我们这届的作业题,见这篇文章

期末复习时又写了一个纯数组实现的树的版本,代码如下:

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    int id;       // 编号
    int flowrate; // 人流量
    int child[3]; // 孩子结点的下标
} node;

node tree[1000];  // 树
int node_pos = 2; // 结点数目
int gate[1000];   // 登机口结点的下标
int gate_num;

int cmp(const void *a, const void *b);
void bfs();

int main()
{
    int father_id;
    tree[1].id = 100; // 第一个结点的ID是确定的

    while (scanf("%d", &father_id) != EOF)
    {
        if (father_id == -1)
            break;
        int father_location; // 找到双亲结点的下标
        for (father_location = 1;; father_location++)
        {
            if (tree[father_location].id == father_id)
                break;
        }
        int son_id, child_num = 0;
        while (scanf("%d", &son_id) != EOF)
        {
            if (son_id == -1)
                break;
            tree[father_location].child[child_num++] = node_pos;
            tree[node_pos].id = son_id;
            if (son_id < 100)
            { // 是登机口
                gate[gate_num++] = node_pos;
            }
            node_pos++;
        }
    }

    int gate_id, flowrate;
    while (scanf("%d%d", &gate_id, &flowrate) != EOF)
    {
        for (int loc = 1;; loc++)
        {
            if (tree[loc].id == gate_id)
            {
                tree[loc].flowrate = flowrate;
                break;
            }
        }
    }

    qsort(gate, gate_num, sizeof(int), cmp);
    bfs();
    return 0;
}

int cmp(const void *a, const void *b)
{
    int e1 = *((int *)a);
    int e2 = *((int *)b);
    if (tree[e1].flowrate != tree[e2].flowrate)
        return tree[e2].flowrate - tree[e1].flowrate;
    return tree[e1].id - tree[e2].id;
}

void bfs() // 层次遍历
{
    int num = 0;
    int stack[1000] = {0};
    int front = 0, rear = 1;
    stack[0] = 1;
    while (front < rear)
    {
        int p = stack[front++];
        for (int i = 0; i < 3; i++)
        {

            if (tree[p].child[i] != 0)
                stack[rear++] = tree[p].child[i];
            else
                break;
        }
        if (tree[p].id < 100)
            printf("%d->%d\n", tree[gate[num++]].id, tree[p].id);
    }
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值