城市聚集度
题目描述:
一张地图上有n个城市,城市和城市之间有且只有一条道路相连:要么直接相连,要么通过其它城市中转相连(可中转一次或多次)。
城市与城市之间的道路都不会成环。
当切断通往某个城市i的所有道路后,地图上将分为多个连通的城市群,设该城市的聚集度为DPi (Degree of Polymerization),
DPi= max (城市群1的城市个数,城市群2的城市个数,…城市群 m 的城市个数)。
请找出地图上 DP值Q最小 的城市 (即找到城市,使得DPi = min(DP1.DP2…DPn)
提示:
如果有多个城市都满足条件,这些城市都要找出来 (可能存在多个解)提示:DPi的计算,可以理解为已知一棵树,删除某人节点后;生成的多个子树,求解多人子数节点数的问题.
输入输出描述:
输入描述:
第一行有一个整数N,表示有N个节点。1 <= N <= 1000。
接下来的 N-1 行每行有两个整数x,y,表示城市x与城市y连接。1<= x,y<= N
输出描述:
输出城市的编号。如果有多个,按照编号升序输出。
示例1:
输入:
5
1 2
2 3
3 4
4 5
输出:
3
说明:
切断通往1的所有道路后,形成的城市群为{{2,3,4,5}},其聚集度:DP1=4
切断通往2的所有道路后,形成的城市群为{{1}, {3,4,5}},其聚集度: DP2=max(1, 3)=3
切断通往3的所有道路后,形成的城市群{{1, 2}, {4,5}},其聚集度:DP3 = max(2, 2)= 2。
切断通往4的所有道路后,形成的城市群{{1, 2, 3}, {5},其聚集度:DP4 = max(3,1)= 3。
切断通往5的所有道路后,形成的城市群{{1, 2, 3, 4},其聚集度:DP5 = 4
故切断通往3时,聚集度最小,故输出是3。
示例1:
输入:
6
1 2
2 3
2 4
3 5
3 6
输出:
2 3
说明:
将通往2或者3的所有路径切断,最大城市群数量是3,
其他任意城市切断后,最大城市群数量都比3大,
所以输出2 3。
解题思路:
以示例1为例,分析过程如下图所示:
题意:共有 n 个城市编号为[1, n],每个每个点都和其他点直接或者间接相连,要求找到某个 点 i,断开与该点直接相连的线,这样c,除了 点i 以外,其余的城市就会形成1个或多个相互独立的城市集群,记录下城市数量点最少的城市集群数量和该 点i 即可。
解题思路:
因为需要每个点都尝试,所以需要进行一次循环,断开每个 点i 与其他点的连接;
断开了 点i 之后,对剩余的 n - 1 个点进行分类(即之间相连或者间接相连的点属于同一个集群/类别——并查集)
代码:
// 路径压缩的加权quick-union算法模板
static class UF {
// 记录集群代表元
int[] parent;
// 记录该集群中的元素个数
int[] size;
// 标记最大的一个集群的所包含节点数量
int maxUFCount;
// 标记一共有多少个相互独立的集群
int unionCount;
private UF (int n) {
parent = new int[n + 1];
size = new int[n + 1];
maxUFCount = 1;
unionCount = n;
for (int i = 0; i <= n; i++) {
parent[i] = i;
size[i] = 1;
}
}
// 获取最大集群包含的节点个数
public int getMaxUFCount() {
return maxUFCount;
}
// 获取相互独立的集群数量
public int getUnionCount() {
return unionCount;
}
// 合并两个点所在的集群
public void union (int a, int b) {
int rootA = find(a);
int rootB = find(b);
if (rootA != rootB) {
if (size[rootA] < size[rootB]) {
parent[rootA] = rootB;
size[rootB] += size[rootA];
// 更新最大个集群包含节点数量
maxUFCount = Math.max(maxUFCount, size[rootB]);
} else {
parent[rootB] = rootA;
size[rootA] += size[rootB];
// 更新最大个集群包含节点数量
maxUFCount = Math.max(maxUFCount, size[rootA]);
}
// 合并了两个集群,则集群数量 -1
unionCount--;
}
}
// 查找某个点的代表元是哪个
private int find (int p) {
if (parent[p] == p) {
return p;
}
// 路径压缩
return parent[p] = find(parent[p]);
}
}
public static void main(String[] args) {
// 读取数据
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[][] arr = new int[n][2];
int[] memo = new int[n + 1];
int min = Integer.MAX_VALUE;
for (int i = 0; i < n - 1; i++) {
arr[i][0] = in.nextInt();
arr[i][1] = in.nextInt();
}
// 1~n这些点
for (int i = 1; i <= n; i++) {
// 遍历输入的所有边,并剔除掉包含当前点的边——即隔断该点与其他点的连接
UF uf = new UF(n);
for (int j = 0; j < n; j++) {
if (arr[j][0] != i && arr[j][1] != i) {
uf.union(arr[j][0], arr[j][1]);
}
}
// 剔除掉点 i 之后形成的孤立的城市群中,最大的城市集包含节点数为
memo[i] = uf.getMaxUFCount();
min = Math.min(min, memo[i]);
}
// 输出答案
StringBuilder stringBuilder = new StringBuilder();
for (int i = 1; i <= n; i++) {
if (memo[i] == min) {
stringBuilder.append(i + " ");
}
}
System.out.println("最小:" + min);
System.out.println(stringBuilder.toString());
}
并查集相关题目
发广播_200分_A/B卷复用_并查集/dfs
快递业务站_100分_2023A卷_并查集/dfs
并查集模板代码
// 路径压缩的加权quick-union算法模板
static class UF {
// 记录集群代表元
int[] parent;
// 记录该集群中的元素个数
int[] size;
// 标记最大的一个集群的所包含节点数量
int maxUFCount;
// 标记一共有多少个相互独立的集群
int unionCount;
private UF (int n) {
parent = new int[n + 1];
size = new int[n + 1];
maxUFCount = 1;
unionCount = n;
for (int i = 0; i <= n; i++) {
parent[i] = i;
size[i] = 1;
}
}
// 获取最大集群包含的节点个数
public int getMaxUFCount() {
return maxUFCount;
}
// 获取相互独立的集群数量
public int getUnionCount() {
return unionCount;
}
// 合并两个点所在的集群
public void union (int a, int b) {
int rootA = find(a);
int rootB = find(b);
if (rootA != rootB) {
if (size[rootA] < size[rootB]) {
parent[rootA] = rootB;
size[rootB] += size[rootA];
// 更新最大个集群包含节点数量
maxUFCount = Math.max(maxUFCount, size[rootB]);
} else {
parent[rootB] = rootA;
size[rootA] += size[rootB];
// 更新最大个集群包含节点数量
maxUFCount = Math.max(maxUFCount, size[rootA]);
}
// 合并了两个集群,则集群数量 -1
unionCount--;
}
}
// 查找某个点的代表元是哪个
private int find (int p) {
if (parent[p] == p) {
return p;
}
// 路径压缩
return parent[p] = find(parent[p]);
}
}