当前位置: 首页 > article >正文

【华为OD-E卷 - 异常的打卡记录100分(python、java、c++、js、c)】

【华为OD-E卷 - 异常的打卡记录100分(python、java、c++、js、c)】

题目

考勤记录是分析和考核职工工作时间利用情况的原始依据,也是计算职工工资的原始依据,为了正确地计算职工工资和监督工资基金使用情况,公司决定对员工的手机打卡记录进行异常排查。
如果出现以下两种情况,则认为打卡异常:
实际设备号与注册设备号不一样 或者,同一个员工的两个打卡记录的时间小于60分钟并且打卡距离超过5km。 给定打卡记录的字符串数组 clockRecords(每个打卡记录组成为:工号;时间(分钟);打卡距离(km);实际设备号;注册设备号),返回其中异常的打卡记录(按输入顺序输出)

输入描述

  • 第一行输入为N,表示打卡记录数;

之后的N行为打卡记录,每一行为一条打卡记录。

输出描述

  • 输出异常的打卡记录

备注

  • clockRecords长度 ≤ 1000 clockRecords[i] 格式:{id},{time},{distance},{actualDeviceNumber},{registeredDeviceNumber} id由6位数字组成 time由整数组成,范围为0 ~ 1000 distance由整数组成,范围为0 ~100 actualDeviceNumber与registeredDeviceNumber由思维大写字母组成

用例

用例一:
输入:
2
100000,10,1,ABCD,ABCD
100000,50,10,ABCD,ABCD
输出:
100000,10,1,ABCD,ABCD;100000,50,10,ABCD,ABCD
用例二:
输入:
2
100000,10,1,ABCD,ABCD
100001,80,10,ABCE,ABCE
输出:
null
用例三:
输入:
2
100000,10,1,ABCD,ABCD
100000,80,10,ABCE,ABCD
输出:
100000,80,10,ABCE,ABCD

python解法

  • 解题思路:
  • 数据结构选择:

使用 emp 字典来存储每个员工的最后一次打卡信息。键为员工 ID (eid),值为三元组 (最后一次打卡时间, 最后一次打卡地点, 当前记录的索引)。
使用 result 集合来存储异常记录的索引。
遍历记录:

对于每条记录,首先判断报告地址 (ad) 是否与打卡地址 (rd) 一致,如果不一致,则这条记录是异常的,加入到 result 集合中。
然后,检查该员工的前一次打卡记录,若满足打卡时间差小于 60 分钟且打卡地点相差超过 5 米的条件,则认为这两次打卡是异常的,当前记录与之前的记录都应该加入异常记录集合中。
输出结果:

若存在异常记录,则返回按要求格式化的结果,返回所有异常记录的信息,按照异常记录的索引升序排列。
如果没有异常记录,返回 “null”。

# 输入读取
n = int(input())  # 输入记录数量
cr = [input().split(",") for _ in range(n)]  # 读取n条记录,存储为列表,每条记录是一个列表

def find_abnormal(cr):
    emp = {}  # 用来存储每个员工的最后一次打卡信息
    result = set()  # 用来存储异常记录的索引

    # 遍历所有的打卡记录
    for i in range(len(cr)):
        # 从当前记录中提取员工ID、时间、地点等信息
        eid, t, d, ad, rd = cr[i]
        
        # 1. 判断报告地址与打卡地址是否不一致
        if ad != rd:
            result.add(i)  # 若不一致,加入异常记录集
        
        # 2. 检查是否存在两次打卡时间小于60分钟且地点相差超过5米的情况
        if eid in emp:
            last_t, last_d, idx = emp[eid]  # 获取该员工上次的打卡记录
            
            # 判断时间差小于60分钟且地点差异大于5米
            if int(t) - last_t < 60 and abs(int(d) - last_d) > 5:
                result.add(i)  # 当前记录异常
                result.add(idx)  # 上次记录异常
        
        # 更新该员工的打卡信息
        emp[eid] = (int(t), int(d), i)  # 存储最新的时间、地点和当前记录的索引

    # 如果有异常记录,按照记录索引升序排列并输出
    if result:
        return ";".join(",".join(cr[i]) for i in sorted(result))  # 格式化输出异常记录
    return "null"  # 若无异常记录,返回 "null"

# 输出结果
print(find_abnormal(cr))

java解法

  • 解题思路
  • 此题要求对员工的打卡记录进行分析,找出异常记录。异常的判断标准有两个:

打卡地址与报告地址不一致,这条记录是异常的。
打卡时间差小于 60 分钟且地点差异超过 5 米,这两条记录是异常的。
解题步骤:
数据存储:

empData:一个哈希表,用于存储每个员工的打卡记录。键是员工 ID(eid),值是该员工所有的打卡记录列表。
errorIdx:一个集合,用于存储异常记录的索引。
解析记录:

对于每条记录,首先检查报告地址与打卡地址是否一致。如果不一致,将该记录的索引加入异常记录集合 errorIdx。
然后,遍历每个员工的记录,检查相邻的打卡记录,如果时间差小于 60 分钟且地点差异超过 5 米,则认为这两次打卡是异常的,添加到异常记录集合。
排序和输出:

对每个员工的记录按照时间进行排序,然后进行时间差和地点差的检查。
如果有异常记录,按要求格式化输出异常记录;如果没有,输出 “null”。

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();  // 读取打卡记录的数量
        String[][] logs = new String[n][];  // 用于存储所有打卡记录

        // 读取所有打卡记录
        for (int i = 0; i < n; i++) {
            logs[i] = input.next().split(",");  // 每条记录按逗号分割
        }

        System.out.println(findAnomalies(logs));  // 调用方法找出异常记录并输出
    }

    // 查找异常打卡记录
    public static String findAnomalies(String[][] logs) {
        HashMap<String, List<String[]>> empData = new HashMap<>();  // 存储每个员工的打卡记录
        Set<Integer> errorIdx = new TreeSet<>();  // 存储异常记录的索引

        // 处理所有记录
        for (int i = 0; i < logs.length; i++) {
            String[] log = Arrays.copyOf(logs[i], logs[i].length + 1);  // 复制记录并扩展,添加记录的索引
            log[log.length - 1] = String.valueOf(i);  // 添加当前记录的索引

            // 1. 如果报告地址与打卡地址不一致,加入异常记录
            if (!log[3].equals(log[4])) {
                errorIdx.add(i);  // 将当前记录索引加入异常记录集合
            }

            // 2. 将打卡记录按照员工 ID 存入 empData
            empData.computeIfAbsent(log[0], k -> new ArrayList<>()).add(log);  // 按员工 ID 存储记录
        }

        // 遍历每个员工的打卡记录,查找符合异常条件的记录
        for (String key : empData.keySet()) {
            List<String[]> records = empData.get(key);  // 获取当前员工的打卡记录
            records.sort(Comparator.comparingInt(a -> Integer.parseInt(a[1])));  // 按照时间排序

            // 检查每两条记录之间的时间差和地点差
            for (int i = 0; i < records.size(); i++) {
                int t1 = Integer.parseInt(records.get(i)[1]);  // 第一条记录的时间
                int d1 = Integer.parseInt(records.get(i)[2]);  // 第一条记录的地点

                // 与之后的记录进行比较
                for (int j = i + 1; j < records.size(); j++) {
                    int t2 = Integer.parseInt(records.get(j)[1]);  // 第二条记录的时间
                    int d2 = Integer.parseInt(records.get(j)[2]);  // 第二条记录的地点

                    // 如果时间差大于等于60分钟,则跳出循环
                    if (t2 - t1 >= 60) break;

                    // 如果时间差小于60分钟,且地点差超过5米,认为两条记录异常
                    if (Math.abs(d2 - d1) > 5) {
                        errorIdx.add(Integer.parseInt(records.get(i)[5]));  // 添加第一条记录的索引
                        errorIdx.add(Integer.parseInt(records.get(j)[5]));  // 添加第二条记录的索引
                    }
                }
            }
        }

        // 如果没有异常记录,返回 "null"
        if (errorIdx.isEmpty()) return "null";

        // 格式化输出异常记录
        StringJoiner sj = new StringJoiner(";");
        for (int idx : errorIdx) {
            sj.add(String.join(",", logs[idx]));  // 将异常记录按照要求的格式连接起来
        }
        return sj.toString();  // 返回格式化后的异常记录
    }
}

C++解法

  • 解题思路
更新中

C解法

  • 解题思路

更新中

JS解法

  • 解题思路

  • 问题要求从员工的打卡记录中找出异常记录,异常记录满足以下条件之一:

报告地址和打卡地址不一致。
同一员工的打卡记录之间,时间差小于 60 分钟,且地点差异大于 5 米。
步骤:
输入数据处理:

记录的格式为 [员工ID, 打卡时间, 打卡地点, 预计打卡地点, 报告打卡地点]。
需要遍历每一条记录,并检查打卡地址是否与报告地址一致。
需要将每一条记录按照员工 ID 分类存储,以便后续对同一员工的打卡记录进行时间和地点差的检查。
判断异常条件:

如果报告地址与打卡地址不一致,记录为异常。
对于每个员工的记录,按照打卡时间升序排序,然后检查相邻记录的时间差和地点差。
如果时间差小于 60 分钟且地点差大于 5 米,则这两条记录都标记为异常。
输出:

如果有异常记录,输出异常记录的内容,按要求格式化(记录间用 ; 分隔)。
如果没有异常记录,输出 “null”。

// 找出所有异常的打卡记录
function findAnomalies(logs) {
    // 用 Map 存储员工的所有打卡记录,键是员工ID,值是该员工的所有打卡记录
    const empData = new Map();
    // 用 Set 存储异常记录的索引
    const errorIdx = new Set();

    // 遍历每条记录
    logs.forEach((log, i) => {
        // 复制记录并添加记录的索引,以便后续引用
        const extendedLog = [...log, i.toString()];

        // 1. 如果报告地址与打卡地址不一致,标记异常
        if (extendedLog[3].trim() !== extendedLog[4].trim()) {
            errorIdx.add(i); // 将异常记录的索引添加到 errorIdx
        }

        // 2. 按员工ID将记录分类存储
        if (!empData.has(extendedLog[0])) {
            empData.set(extendedLog[0], []); // 如果没有该员工,初始化为一个空数组
        }
        empData.get(extendedLog[0]).push(extendedLog); // 将记录加入该员工的记录列表
    });

    // 对每个员工的记录进行处理
    for (const key of empData.keys()) {
        const records = empData.get(key);  // 获取该员工的所有打卡记录
        // 按打卡时间升序排序
        records.sort((a, b) => parseInt(a[1]) - parseInt(b[1]));

        // 遍历员工的打卡记录,检查时间和地点差
        for (let i = 0; i < records.length; i++) {
            const t1 = parseInt(records[i][1]); // 获取第一个记录的时间
            const d1 = parseInt(records[i][2]); // 获取第一个记录的地点

            // 与之后的记录进行比较
            for (let j = i + 1; j < records.length; j++) {
                const t2 = parseInt(records[j][1]); // 获取第二个记录的时间
                const d2 = parseInt(records[j][2]); // 获取第二个记录的地点

                // 如果时间差大于等于60分钟,则跳出循环
                if (t2 - t1 >= 60) break;

                // 如果时间差小于60分钟,且地点差超过5米,认为两条记录异常
                if (Math.abs(d2 - d1) > 5) {
                    // 标记两条记录为异常
                    errorIdx.add(parseInt(records[i][5]));
                    errorIdx.add(parseInt(records[j][5]));
                }
            }
        }
    }

    // 如果没有异常记录,返回 "null"
    if (errorIdx.size === 0) return "null";

    // 格式化输出异常记录
    const result = Array.from(errorIdx)
        .map(idx => logs[idx].join(",")) // 将每条异常记录转换为字符串
        .join(";");  // 用分号连接所有异常记录
    return result;  // 返回格式化后的异常记录
}

// 读取输入数据并调用 findAnomalies 方法
const readline = require('readline');
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

// 询问输入的记录数量 n
rl.question('', (n) => {
    const logs = [];
    let count = 0;

    // 监听每一行输入
    rl.on('line', (input) => {
        if (input.trim() === '') return;  // 如果输入为空则跳过
        logs.push(input.split(","));  // 将输入的记录按逗号分割,保存到 logs 数组
        count++;
        if (count === parseInt(n)) {  // 如果输入的记录数量达到 n
            console.log(findAnomalies(logs));  // 调用 findAnomalies 输出结果
            rl.close();  // 关闭输入流
        }
    });
});

注意:

如果发现代码有用例覆盖不到的情况,欢迎反馈!会在第一时间修正,更新。
解题不易,如对您有帮助,欢迎点赞/收藏


http://www.kler.cn/a/463444.html

相关文章:

  • 【蓝桥杯比赛-C++组-经典题目汇总】
  • 【开源社区openEuler实践】hpcrunner
  • mysql 忘记root密码 无密码登录系统 配置文件怎么改?
  • 沙箱模拟支付宝支付3--支付的实现
  • 感知机参数更新策略
  • 利用 AI 高效生成思维导图的简单实用方法
  • python数据分析:使用pandas库读取和编辑Excel表
  • 期末速成C++【知识点汇总完】
  • vite+vue3项目启动报错
  • windows C#-声明、实例化和使用委托
  • 青少年编程与数学 02-005 移动Web编程基础 10课题、设备调用
  • 创建线程的8种方法
  • 计算机网络原理(谢希仁第八版)第4章课后习题答案
  • 在 Swift 中使用 SQL 组合人员和地址数据
  • ChatGPT 与 AGI:人工智能的当下与未来走向全解析
  • UE5材质节点Frac/Fmod
  • NestJS 中间件与拦截器:请求处理流程详解
  • 前端安全措施:接口签名、RSA加密、反调试、反反调试、CAPTCHA验证
  • Go 语言 API 限流实战:保障系统稳定性的护盾
  • 2024年终总结:非常充实的一年
  • Three.js教程007:响应式画布与全屏控制
  • 深度学习每周学习总结R2(RNN-天气预测)
  • Postman[5] 环境变量和全局变量
  • 虚拟机Centos下安装Mysql完整过程(图文详解)
  • 快速掌握Elasticsearch检索之二:滚动查询(scrool)获取全量数据(golang)
  • Dockerfile 构建继承父镜像的 ENTRYPOINT 和 CMD