华为机试:找城市

【编程题目 |200分】 找城市【2022 Q1,Q2 考试题】

题目描述

一个城市规划问题,一个地图有很多城市,两个城市之间只有一种路径,切断通往一个城市i的所有路径之后,其他的城市形成了独立的城市群,这些城市群里最大的城市数量,就是聚集度DPi,现在给出一个地图上各个城市的路径,输出聚集度最小的城市,如果有多个结果,按照编号从小到大

输入描述

第一行输入 城市节点数目N
后面N-1输入城市之间的路径

输出描述

聚集度最小的城市

示例
输入
5
1 2
2 3
3 4
4 5
输出
3
说明
将通往3的所有路径切断,最大城市群数量是2,其他任意城市切断后,最大城市群数量都比2大,所以输出3
输入
6
1 2
2 3
2 4
3 5
3 6
输出
2 3
说明
将通往2或者3的所有路径切断,最大城市群数量是3,其他任意城市切断后,最大城市群数量都比3大,所以输出2 3
思路分析

题目中描述切断通往一个城市i的所有路径之后,其他的城市形成了独立的城市群,这比较容易想到并查集,两个城市相连,就把两个城市合并。

循环n次遍历所有城市的结果,每个城市遍历所有的链接信息,如果出现当前循环需要排除的城市,则不执行本次合并操作。

需要注意的是:

  1. 并查集模板中初始化时,需要从1开始,因为城市的编号是从1开始。
  2. 统计每个城市所在的最大的城市数量,即聚集度

并查集方法不要害怕,因为并查集类是有固定模板,主函数一般只需要将相连的两个合并操作即可,最后统计数量。可以参考:【Leetcode】并查集(Union-Find)算法

参考代码
import java.util.Scanner;

public class jujidu {
    static class UF {  // 路径压缩的加权quick-union算法模板
        int N;
        int count;
        int[] id;
        int[] sz;

        private UF (int n) {
            N = n;
            count = n;
            id = new int[n + 1];
            sz = new int[n + 1];
            for (int i = 1; i <= n; i++) {  // 这里需要注意,城市是从1开始的
                id[i] = i;
                sz[i] = 1;
            }
        }

        public int getMax () {  // 统计并查集的最大值
            int max = 0;
            for (int i = 1; i < sz.length; i++) {
                max = Math.max(max, sz[i]);
            }
            return max;
        }

        public void union (int p, int q) {
            int pRoot = find(p);
            int qRoot = find(q);
            if (pRoot != qRoot) {
                if (sz[pRoot] < sz[qRoot]) {
                    id[pRoot] = qRoot;
                    sz[qRoot] += sz[pRoot];
                } else {
                    id[qRoot] = pRoot;
                    sz[pRoot] += sz[qRoot];
                }
                count--;
            }
        }

        private int find (int p) {
            if (p == id[p]) {
                return p;
            }
            id[p] = find(id[p]);
            return id[p];
        }
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[][] arr = new int[n][2];
        for (int i = 0; i < n - 1; i++) {
            arr[i][0] = in.nextInt();
            arr[i][1] = in.nextInt();
        }
        // 并查集
        int res = Integer.MAX_VALUE;  // 统计最小聚集度
        int[] maxArray = new int[n + 1];  // 统计每个城市的聚集度
        for (int i = 1; i <= n; i++) {  // 对于每一个城市
            UF uf = new UF(n);
            for (int j = 0; j < n - 1; j++) {  // 判断每一条路径
                if (arr[j][0] == i || arr[j][1] == i)  {
                    continue;
                } else {
                    uf.union(arr[j][0], arr[j][1]);
                }
            }
            maxArray[i] = uf.getMax();  // 每个城市对应的聚集度
            res = Math.min(res, maxArray[i]);  // 切掉路径后的最小聚集度
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i < maxArray.length; i++) {
            if (maxArray[i] == res) {
                sb.append(i).append(" ");
            }
        }
        sb.setLength(sb.length() - 1);
        System.out.println(sb);
    }
}

欢迎在评论区指正以及留下自己的更简洁的方法。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值