2024年华为OD笔试机试E卷- 5G网络建设 (java/c++/python)

华为OD机试E卷2024真题目录(java & c++ & python)

本人习惯先看输入输出描述,可以明确知道哪些数据已知,需要去得到什么结果,再代入更有目的性地阅读题干内容,快速理解,所以把输入输出描述放在前面,你可以试下这样阅读对你是否有帮助。

输入描述

第一行输入表示基站的个数N,其中:

0 < N ≤ 20
第二行输入表示具备光纤直连条件的基站对的数目M,其中:

0 < M < N * (N - 1) / 2
从第三行开始连续输入M行数据,格式为

X Y Z P

其中:

X,Y 表示基站的编号

0 < X ≤ N
0 < Y ≤ N
X ≠ Y
Z 表示在 X、Y之间架设光纤的成本

0 < Z < 100
P 表示是否已存在光纤连接,0 表示未连接,1表示已连接

输出描述

如果给定条件,可以建设成功互联互通的5G网络,则输出最小的建设成本

如果给定条件,无法建设成功互联互通的5G网络,则输出 -1

题目描述

现需要在某城市进行5G网络建设,已经选取N个地点设置5G基站,编号固定为1到N,接下来需要各个基站之间使用光纤进行连接以确保基站能互联互通,不同基站之间假设光纤的成本各不相同,且有些节点之间已经存在光纤相连。

请你设计算法,计算出能联通这些基站的最小成本是多少。

注意:基站的联通具有传递性,比如基站A与基站B架设了光纤,基站B与基站C也架设了光纤,则基站A与基站C视为可以互相联通。

用例1

输入
3
3
1 2 3 0
1 3 1 0
2 3 5 0

输出
4

说明 只需要在1,2以及1,3基站之间铺设光纤,其成本为3+1=4在这里插入图片描述

用例2

输入
3
1
1 2 5 0

输出
-1

说明 3基站无法与其他基站连接,输出-1在这里插入图片描述

用例3

输入
3
3
1 2 3 0
1 3 1 0
2 3 5 1

输出
1

说明 2,3基站已有光纤相连,只要在1,3基站之间铺设光纤,其成本为1在这里插入图片描述

解题思路

最小生成树,Prim算法或者Kruskal算法都可以。
区别于板子题,本题中主要是存在一些已经关联好的节点,对于已经关联了的节点,可以认为他们之间的边权为0。这样的话,本题就变成最小生成树的模板题了。

C++、Java、Python参考代码如下:

Java

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

public class Main {
    // 边类
    static class Edge {
        int from;   // 边的起点
        int to;     // 边的终点
        int weight; // 边的权重

        public Edge(int from, int to, int weight) {
            this.from = from;
            this.to = to;
            this.weight = weight;
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int n = scanner.nextInt(); // 基站数量(节点数)
        int m = scanner.nextInt(); // 基站对数量(边数)

        Edge[] edges = new Edge[m];

        for (int i = 0; i < m; i++) {
            int x = scanner.nextInt();
            int y = scanner.nextInt();
            int z = scanner.nextInt();
            int p = scanner.nextInt();

            // 如果p为1,则将x-y边的权重视为0
            edges[i] = new Edge(x, y, p == 0 ? z : 0);
        }

        System.out.println(kruskal(edges, n)); // 输出最小生成树的权重
    }

    // Kruskal算法实现
    public static int kruskal(Edge[] edges, int n) {
        int minWeight = 0;

        // 按照边权升序排序
        Arrays.sort(edges, (a, b) -> a.weight - b.weight);

        UnionFindSet ufs = new UnionFindSet(n + 1);

        // 遍历所有边
        for (Edge edge : edges) {
            // 检查是否在同一连通分量
            if (ufs.find(edge.from) != ufs.find(edge.to)) {
                minWeight += edge.weight; // 累加边的权重
                ufs.union(edge.from, edge.to); // 合并连通分量

                // 如果只剩下两个连通分量,返回最小生成树的权重
                if (ufs.count == 2) {
                    return minWeight;
                }
            }
        }

        return -1; // 如果无法形成最小生成树
    }
}

// 并查集类
class UnionFindSet {
    int[] fa;   // 记录父节点
    int count;  // 记录连通分量的数量

    public UnionFindSet(int n) {
        this.fa = new int[n];
        this.count = n;
        for (int i = 0; i < n; i++) {
            this.fa[i] = i; // 初始化每个节点的父节点为自身
        }
    }

    // 查找操作
    public int find(int x) {
        if (x != this.fa[x]) {
            this.fa[x] = this.find(this.fa[x]); // 路径压缩
        }
        return x;
    }

    // 合并操作
    public void union(int x, int y) {
        int xFa = this.find(x);
        int yFa = this.find(y);

        if (xFa != yFa) {
            this.fa[yFa] = xFa; // 合并两个集合
            this.count--; // 更新连通分量数量
        }
    }
}


C++

#include <bits/stdc++.h>

using namespace std;

// 并查集类
class UnionFindSet {
public:
    vector<int> parent; // 存储每个节点的父节点
    int componentCount; // 连接分量数量

    explicit UnionFindSet(int n) {
        parent.resize(n);
        componentCount = n;
        for (int i = 0; i < n; i++) parent[i] = i; // 初始化父节点为自身
    }

    // 查找节点的根节点,并进行路径压缩
    int find(int x) {
        if (x != parent[x]) {
            parent[x] = find(parent[x]); // 路径压缩
        }
        return parent[x];
    }

    // 合并两个节点所在的集合
    void merge(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);

        if (rootX != rootY) {
            parent[rootY] = rootX; // 将y的根节点指向x
            componentCount--; // 连接分量数量减少
        }
    }
};

// 边类
class Edge {
public:
    int from;   // 边的起点
    int to;     // 边的终点
    int weight; // 边的权重

    Edge(int from, int to, int weight) : from(from), to(to), weight(weight) {}
};

int n; // 节点数量
vector<Edge> edges; // 边列表

// Kruskal算法实现
int kruskal() {
    int totalWeight = 0; // 最小生成树的总权重

    // 按边权升序排序
    sort(edges.begin(), edges.end(), [](const Edge &a, const Edge &b) {
        return a.weight < b.weight;
    });

    UnionFindSet ufs(n + 1); // 初始化并查集

    for (const auto &edge : edges) {
        // 检查是否形成环
        if (ufs.find(edge.from) != ufs.find(edge.to)) {
            totalWeight += edge.weight; // 加入边权
            ufs.merge(edge.from, edge.to); // 合并集合

            // 如果只剩下两个连通分量,返回结果
            if (ufs.componentCount == 2) {
                return totalWeight;
            }
        }
    }

    return -1; // 无法形成最小生成树
}

int main() {
    cin >> n; // 输入节点数量
    int m; // 边数量
    cin >> m;

    for (int i = 0; i < m; i++) {
        int x, y, z, p;
        cin >> x >> y >> z >> p;

        // 如果p==1,则边权为0
        edges.emplace_back(x, y, p == 0 ? z : 0);
    }

    cout << kruskal() << endl; // 输出最小生成树的权重

    return 0;
}


Python

# 并查集实现
class UnionFindSet:
    def __init__(self, n):
        self.parent = [i for i in range(n)]  # 初始化父节点
        self.count = n  # 记录连通分量数量

    def find(self, x):
        if x != self.parent[x]:  # 路径压缩
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]

    def union(self, x, y):
        x_root = self.find(x)
        y_root = self.find(y)

        if x_root != y_root:  # 合并不同的连通分量
            self.parent[y_root] = x_root
            self.count -= 1


# 输入获取
n = int(input())  # 基站数量(节点数)
m = int(input())  # 基站对数量(边数)

edges = []
for _ in range(m):
    x, y, z, p = map(int, input().split())
    # 如果未关联,添加边权重;如果已关联,权重为0
    edges.append([x, y, 0 if p else z])


# Kruskal算法实现
def kruskal():
    min_weight = 0

    # 按照边权升序排序
    edges.sort(key=lambda edge: edge[2])

    ufs = UnionFindSet(n + 1)  # 创建并查集

    for x, y, weight in edges:
        # 只有当x和y不在同一连通分量时才能合并
        if ufs.find(x) != ufs.find(y):
            min_weight += weight
            ufs.union(x, y)

            # 如果只剩两个连通分量,返回当前最小权重
            if ufs.count == 2:
                return min_weight

    return -1  # 如果无法形成最小生成树


# 算法入口
print(kruskal())


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算法之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值