精准核酸检测 - 华为OD统一考试

发布时间:2024年01月22日

OD统一考试(C卷)

分值: 100分

题解: Java / Python / C++

alt

题目描述

为了达到新冠疫情精准防控的需要,为了避免全员核酸检测带来的浪费,需要精准圈定可能被感染的人群。

现在根据传染病流调以及大数据分析,得到了每个人之间在时间、空间上是否存在轨迹的交叉。

现在给定一组确诊人员编号(X1,X2,X3…Xn) 在所有人当中,找出哪些人需要进行核酸检测,输出需要进行核酸检测的人数。(注意:确诊病例自身不需要再做核酸检测)

需要进行核酸检测的人,是病毒传播链条上的所有人员,即有可能通过确诊病例所能传播到的所有人。

例如:A是确诊病例,A和B有接触、B和C有接触 C和D有接触,D和E有接触。那么B、C、D、E都是需要进行核酸检测的。

输入描述

第一行为总人数N

第二行为确诊病例人员编号 (确证病例人员数量 < N) ,用逗号隔开

接下来N行,每一行有N个数字,用逗号隔开,其中第i行的第个j数字表名编号i是否与编号j接触过。0表示没有接触,1表示有接触

输出描述

输出需要做核酸检测的人数

补充说明

  • 人员编号从0开始
  • 0 < N < 100

示例1

输入:
5
1,2
1,1,0,1,0
1,1,0,0,0
0,0,1,0,1
1,0,0,1,0
0,0,1,0,1

输出
3

说明:
编号为1、2号的人员为确诊病例1号与0号有接触,0号与3号有接触,2号54号有接触。所以,需要做核酸检测的人是0号、3号、4号,总计3人要进行核酸检测。

题解

经典的连通问题,这里使用并查集的简单模板套用即可解决。

如果对并查集不会,可以通过 https://zhuanlan.zhihu.com/p/93647900 来学习。

Java

import java.util.Arrays;
import java.util.Scanner;

/**
 * @author code5bug
 */
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = Integer.parseInt(in.nextLine());
        // 确诊人员编号
        int[] confirm = Arrays.stream(in.nextLine().split(",")).mapToInt(Integer::parseInt).toArray();

        // 使用并查集将所有确诊的人员合并成一组
        int start = confirm[0];
        UnionFind uf = new UnionFind(n);
        for (int i = 1; i < confirm.length; i++) {
            uf.merge(start, confirm[i]);
        }

        // 将所有有接触的人进行合并操作
        for (int i = 0; i < n; i++) {
            int[] row = Arrays.stream(in.nextLine().split(",")).mapToInt(Integer::parseInt).toArray();
            for (int j = 0; j < row.length; j++) {
                if (row[j] == 1) {
                    uf.merge(i, j);
                }
            }
        }

        int cnt = 0; // 已经确认的总人数
        for (int i = 0; i < n; i++) {
            if (uf.find(i) == uf.find(start)) cnt++;
        }

        // 输出, 这里排除了确诊病例自身不需要再做核酸检测人
        System.out.println(cnt - confirm.length);
    }
}

class UnionFind {
    // father[2] = 3 表示元素2的父节点是3
    public int[] father;

    public UnionFind(int len) {
        father = new int[len + 1];
        for (int i = 0; i <= len; i++) {
            father[i] = i;
        }
    }

    // 查询 x 的根节点
    public int find(int x) {
        if (x < 0 || x >= father.length) {
            throw new RuntimeException("查询越界");
        }

        // 合并(路径压缩)
        return (x == father[x] ? x : (father[x] = find(father[x])));
    }

    // 合并节点, y 的根节点指向 x 的根节点
    public void merge(int x, int y) {
        int xRoot = find(x), yRoot = find(y);
        father[yRoot] = xRoot;
    }
}

Python

class UnionFind:
    def __init__(self, length):
        self.father = list(range(length + 1))

    def find(self, x):
        if not (0 <= x < len(self.father)):
            raise ValueError("查询越界")

        # 合并(路径压缩)
        if x != self.father[x]:
            self.father[x] = self.find(self.father[x])
        return self.father[x]

    def merge(self, x, y):
        x_root, y_root = self.find(x), self.find(y)
        self.father[y_root] = x_root


def main():
    n = int(input())
    confirm = list(map(int, input().split()))

    # 使用并查集将所有确诊的人员合并成一组
    start = confirm[0]
    uf = UnionFind(n)
    for i in range(1, len(confirm)):
        uf.merge(start, confirm[i])

    # 将所有有接触的人进行合并操作
    for i in range(n):
        row = list(map(int, input().split()))
        for j in range(len(row)):
            if row[j] == 1:
                uf.merge(i, j)

    cnt = 0  # 已经确认的总人数
    for i in range(n):
        if uf.find(i) == uf.find(start):
            cnt += 1

    # 输出, 这里排除了确诊病例自身不需要再做核酸检测人
    print(cnt - len(confirm))


if __name__ == "__main__":
    main()

C++

#include <iostream>
#include <vector>
#include <sstream>

using namespace std;

class UnionFind {
public:
    vector<int> father;

    UnionFind(int len) {
        father.resize(len + 1);
        for (int i = 0; i <= len; i++) {
            father[i] = i;
        }
    }

    int find(int x) {
        if (x < 0 || x >= father.size()) {
            throw out_of_range("查询越界");
        }

        return (x == father[x] ? x : (father[x] = find(father[x])));
    }

    void merge(int x, int y) {
        int xRoot = find(x), yRoot = find(y);
        father[yRoot] = xRoot;
    }
};

int main() {
    int n;
    cin >> n;
    cin.ignore();  // 消耗掉换行符

    string confirmInput;
    getline(cin, confirmInput);

    stringstream confirmStream(confirmInput);
    vector<int> confirm;
    int confirmNum;
    while (confirmStream >> confirmNum) {
        confirm.push_back(confirmNum);
        if (confirmStream.peek() == ',') {
            confirmStream.ignore();
        }
    }

    int start = confirm[0];
    UnionFind uf(n);

    for (size_t i = 1; i < confirm.size(); i++) {
        uf.merge(start, confirm[i]);
    }

    for (int i = 0; i < n; i++) {
        string rowInput;
        getline(cin, rowInput);

        stringstream rowStream(rowInput);
        int contact;
        for (int j = 0; j < n; j++) {
            rowStream >> contact;
            if (contact == 1) {
                uf.merge(i, j);
            }
            if (rowStream.peek() == ',') {
                rowStream.ignore();
            }
        }
    }

    int cnt = 0;
    for (int i = 0; i < n; i++) {
        if (uf.find(i) == uf.find(start)) {
            cnt++;
        }
    }

    cout << cnt - confirm.size() << endl;

    return 0;
}

(并查集)相关练习题

题号题目难易
LeetCode 12021202. 交换字符串中的元素中等
LeetCode 17221722. 执行交换操作后的最小汉明距离中等
LeetCode 947947. 移除最多的同行或同列石头中等
LeetCode 924924. 尽量减少恶意软件的传播困难

????华为OD机试面试交流群每日真题分享): 加V时备注“华为od加群”

🙏整理题解不易, 如果有帮助到您,请给点个赞 ???? 和收藏 ?,让更多的人看到。🙏🙏🙏

文章来源:https://blog.csdn.net/user_longling/article/details/135739373
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。