5G网络建设

一、题目描述

现需要在某城市计划建设 5G 网络,选取了 N 个地点部署基站(编号 1~N,0 < N ≤ 20)。需通过光纤连接基站实现互联互通,不同基站间架设光纤的成本不同,且部分基站间已存在光纤连接。要求设计算法计算联通所有基站的最小建设成本

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

二、输入输出描述

输入描述

  • 第一行:基站总数 N(0 < N ≤ 20);
  • 第二行:具备光纤直连条件的基站对数目 M(0 < M < N*(N-1)/2);
  • 后续 M 行:每行格式为 X Y Z P,其中:
    • X,Y:基站编号(0 < X,Y ≤ N,X≠Y);
    • Z:X、Y 间架设光纤的成本(0 < Z < 100);
    • P:是否已存在光纤连接(0 = 未连接,1 = 已连接)。

输出描述

  • 如果给定条件,可以建设成功互联互通的5G网络,则输出最小的建设成本;
  • 如果给定条件,无法建设成功互联互通的5G网络,则输出 -1。

三、示例

|----|--------------------------------|
| 输入 | 3 3 1 2 3 0 1 3 1 0 2 3 5 0 |
| 输出 | 4 |
| 说明 | 只需要在1,2以及1,3基站之间铺设光纤,其成本为3+1=4 |

|----|-------------------|
| 输入 | 3 1 1 2 5 0 |
| 输出 | -1 |
| 说明 | 3基站无法与其他基站连接,输出-1 |

|----|----------------------------------|
| 输入 | 3 3 1 2 3 0 1 3 1 0 2 3 5 1 |
| 输出 | 1 |
| 说明 | 2,3基站已有光纤相连,只要在1,3基站之间铺设光纤,其成本为1 |

四、解题思路

1. 核心思想

核心是克鲁斯卡尔(Kruskal)算法 + 并查集(Union-Find)

  1. 问题本质是带权无向图的最小生成树(MST) 问题:基站为节点,光纤为边,成本为边权,已连接的边权为 0;
  2. 并查集用于高效管理节点的联通性,判断新增边是否会形成环;
  3. 按边权升序遍历所有边,依次将边加入生成树(无环则合并节点),直到所有节点联通;
  4. 累加新增边的权值(已连接的边权为 0,无成本),最终若形成最小生成树则输出总成本,否则输出 -1

2. 问题本质分析

  • 问题本质是最小生成树求解
    1. 已存在的光纤(P=1)可视为 "零成本" 边,优先加入生成树;
    2. 未连接的光纤(P=0)按成本升序排序,尽可能选择低成本边连接分散的联通分量;
    3. 并查集是解决最小生成树问题的高效工具,可在 O (α(N))(近似常数)时间内完成节点合并与联通性查询;
    4. 核心约束:N≤20,算法时间复杂度无性能压力,优先保证逻辑清晰。

3. 核心逻辑

  1. 边预处理
    • 将所有基站对(边)转换为统一格式,已连接的边(P=1)权值设为 0,未连接的边权值为 Z;
    • 按边权升序排序(优先选零成本、低成本边);
  2. 并查集初始化:每个基站初始为独立的联通分量(父节点为自身);
  3. Kruskal 算法执行
    • 遍历排序后的边,对每条边的两个节点,若不在同一联通分量则合并,并累加边权到总成本;
    • 若所有节点已联通,提前终止遍历;
  4. 联通性校验 :遍历结束后,检查所有节点是否属于同一联通分量,是则输出总成本,否则输出 -1

4. 步骤拆解

  1. 输入解析:读取 N、M,以及 M 条边的信息,转换为边对象(含起点、终点、权值);
  2. 边预处理
    • 对每条边,若 P=1 则权值设为 0,否则为 Z;
    • 按权值升序排序所有边;
  3. 并查集初始化:创建父节点数组,parent [i] = i(i 为基站编号);
  4. 最小生成树构建
    • 初始化总成本为 0,已选边数为 0;
    • 遍历排序后的边:
      • 查找两个节点的根节点,若不同则合并,累加边权到总成本,已选边数 + 1;
      • 若已选边数 == N-1(生成树边数 = 节点数 - 1),提前退出;
  5. 结果校验
    • 检查所有节点是否联通(根节点相同);
    • 联通则输出总成本,否则输出 -1

五、代码实现

java 复制代码
import java.util.*;

public class Min5GNetworkCost {
    // 并查集父节点数组(基站编号1~N,数组长度N+1)
    private static int[] parent;

    // 边对象:存储起点、终点、权值
    static class Edge implements Comparable<Edge> {
        int from;
        int to;
        int weight;

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

        // 按权值升序排序
        @Override
        public int compareTo(Edge o) {
            return Integer.compare(this.weight, o.weight);
        }
    }

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

        // 1. 输入解析
        int N = scanner.nextInt(); // 基站总数
        int M = scanner.nextInt(); // 边数
        scanner.nextLine(); // 吸收换行

        List<Edge> edges = new ArrayList<>();
        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)则权值为0,否则为Z
            int weight = P == 1 ? 0 : Z;
            edges.add(new Edge(X, Y, weight));
        }
        scanner.close();

        // 2. 初始化并查集
        parent = new int[N + 1];
        for (int i = 1; i <= N; i++) {
            parent[i] = i;
        }

        // 3. 按边权升序排序
        Collections.sort(edges);

        // 4. 执行Kruskal算法
        int totalCost = 0;
        int selectedEdges = 0; // 已选边数(生成树需N-1条边)

        for (Edge edge : edges) {
            int rootFrom = find(edge.from);
            int rootTo = find(edge.to);

            // 两个节点不在同一联通分量,合并
            if (rootFrom != rootTo) {
                union(rootFrom, rootTo);
                totalCost += edge.weight;
                selectedEdges++;

                // 生成树已完成(N-1条边),提前退出
                if (selectedEdges == N - 1) {
                    break;
                }
            }
        }

        // 5. 校验是否所有基站联通
        boolean isConnected = true;
        int root = find(1); // 以第一个基站为基准
        for (int i = 2; i <= N; i++) {
            if (find(i) != root) {
                isConnected = false;
                break;
            }
        }

        // 6. 输出结果
        if (isConnected) {
            System.out.println(totalCost);
        } else {
            System.out.println(-1);
        }
    }

    // 并查集:查找根节点(路径压缩)
    private static int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]); // 路径压缩,优化后续查询
        }
        return parent[x];
    }

    // 并查集:合并两个节点(按根节点合并)
    private static void union(int x, int y) {
        parent[y] = x;
    }
}
相关推荐
机器学习之心2 小时前
最小二乘支持向量机(LSSVM)结合遗传算法(GA)解决单目标优化问题,MATLAB代码
算法·支持向量机·matlab·单目标优化问题
码界奇点2 小时前
基于Spring Boot的后台管理系统设计与实现
java·spring boot·后端·车载系统·毕业设计·源代码管理
爱敲点代码的小哥2 小时前
json序列化和反序列化 和 数组转成json格式
java·前端·json
零度念者2 小时前
Java IO/NIO 深度解析:从底层原理到高性能图片网关实战
java·nio
没有故事的Zhang同学2 小时前
09-🔍数据结构与算法核心知识 | 二叉搜索树:有序数据结构理论与实践
算法
这是程序猿2 小时前
基于java的ssm框架经典电影推荐网站
java·开发语言·spring boot·spring·经典电影推荐网站
酸菜牛肉汤面2 小时前
5、索引的数据结构(b+树,hash)
数据结构·b树·哈希算法
Nan_Shu_6142 小时前
学习:Java (1)
java·开发语言·学习