2023华为OD机试 (B卷)|200分 城市聚集度(C++ Java JavaScript Python)
题目描述
一张地图上有N
个城市,城市和城市之间有且只有一条道路相连,要么直接相连,要么通过其他城市中转相连(可中转一次或多次)。城市与城市之间的道路都不会成环。
当切断通往某城市i的所有道路后,地图上将分成多个连通的城市群,设该城市i的聚集度为DPi of Polymerization), DPi = max(城市群1的城市个数,城市群2的城市个数,...城市群m的城市个数)
。 请找出地图上DP值最小的城市(即找到城市j,使得DPj = min(DP1,DP2...DPn))
提示:如果有多个城市都满足条件,这些城市都要找出来(可能存在多个解)
提示:DPi
的计算,可以理解为已知一个树,删除某个节点后,生成的多个字树,求解多个字树节点数的问题。
输入描述
每个样例,第一行有一个整数N,表示有
N
个节点,1 <=N <=1000
j接下来的N-1
行每行有两个整数x,y
,表示城市x与城市y连接。1 <= x , y <= N
输出描述
输出城市的编号,如果有多个,按照编号升序输出。
用例
输入 | 5 1 2 2 3 3 4 4 5 |
---|---|
输出 | 3 |
说明
对于城市
3
,切断通往3
的所有道路后,形成2
个城市群[(1,2),(4,5)]
,其聚集度分别都是2,。DP3 = 2.
对于城市4,切断通往城市4的所有道路后,形成2个城市群[(1,2,3),(5)],DP4 = max(3,1) = 3
以此类推,切断其他城市的所有道路后,得到的DP
都会大于2
,因为城市3
就是满足条件的城市,输出是3
。
Java
import java.util.*;
import java.lang.*;
import java.io.*;
class Main {
static class UnionFindSet {
private int[] father; // 存储每个节点的父节点
public UnionFindSet(int n) { // 初始化并查集,每个节点的父节点为自己
father = new int[n];
for (int i = 0; i < n; i++) father[i] = i;
}
public int find(int x) { // 查找x的祖先节点,路径压缩优化
if (father[x] != x) {
father[x] = find(father[x]);
}
return father[x];
}
public void unionSet(int x, int y) { // 合并x和y所在的集合
int x_fa = find(x);
int y_fa = find(y);
if (x_fa != y_fa) { // 如果x和y不在同一个集合中,则将y的祖先节点设为x的祖先节点
father[y_fa] = x_fa;
}
}
}
public static void main(String[] args) throws java.lang.Exception {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] relations = new int[n - 1][2]; // 存储n-1条关系
for (int i = 0; i < n - 1; i++) { // 输入n-1条关系
relations[i][0] = sc.nextInt();
relations[i][1] = sc.nextInt();
}
int min_dp = Integer.MAX_VALUE; // 最小的最大连通块大小
List<Integer> city = new ArrayList<>(); // 最小的最大连通块所在的城市
for (int i = 1; i <= n; i++) { // 枚举每个城市作为特殊城市
UnionFindSet ufs = new UnionFindSet(n + 1); // 初始化并查集
for (int[] relation : relations) { // 将与特殊城市相连的边删除
int x = relation[0], y = relation[1];
if (x == i || y == i) continue;
ufs.unionSet(x, y);
}
Map<Integer, Integer> count = new HashMap<>(); // 统计每个连通块的大小
for (int f : ufs.father) {
f = ufs.find(f);
count.put(f, count.getOrDefault(f, 0) + 1);
}
int dp = 0; // 最大连通块大小
for (Map.Entry<Integer, Integer> entry : count.entrySet()) {
dp = Math.max(dp, entry.getValue());
}
if (dp < min_dp) { // 如果当前最大连通块大小比之前的最小值还小,则更新最小值和最小值所在的城市
min_dp = dp;
city.clear();
city.add(i);
}
else if (dp == min_dp) { // 如果当前最大连通块大小与之前的最小值相等,则将城市加入最小值所在的城市列表
city.add(i);
}
}
for (int c : city) { // 输出最小的最大连通块所在的城市
System.out.print(c + " ");
}
}
}
Javascript
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
class UnionFindSet {
constructor(n) {
this.father = new Array(n);
for (let i = 0; i < n; i++) {
this.father[i] = i;
}
}
find(x) {
if (this.father[x] !== x) {
this.father[x] = this.find(this.father[x]);
}
return this.father[x];
}
unionSet(x, y) {
const x_fa = this.find(x);
const y_fa = this.find(y);
if (x_fa !== y_fa) {
this.father[y_fa] = x_fa;
}
}
}
let n;
let relations = [];
rl.on('line', (line) => {
if (!n) {
n = parseInt(line);
} else {
const [x, y] = line.split(' ').map(Number);
relations.push([x, y]);
if (relations.length === n - 1) {
rl.close();
}
}
});
rl.on('close', () => {
let min_dp = Infinity;
let city = [];
for (let i = 1; i <= n; i++) {
const ufs = new UnionFindSet(n + 1);
for (const [x, y] of relations) {
if (x === i || y === i) continue;
ufs.unionSet(x, y);
}
const count = new Map();
for (let f of ufs.father) {
f = ufs.find(f);
count.set(f, (count.get(f) || 0) + 1);
}
let dp = 0;
for (const c of count.values()) {
dp = Math.max(dp, c);
}
if (dp < min_dp) {
min_dp = dp;
city = [i];
} else if (dp === min_dp) {
city.push(i);
}
}
console.log(city.join(' '));
});
C++
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <climits>
using namespace std;
class UnionFindSet {
public:
vector<int> father; // 存储每个节点的父节点
UnionFindSet(int n) { // 初始化并查集,每个节点的父节点为自己
father.resize(n);
for (int i = 0; i < n; i++) father[i] = i;
}
int find(int x) { // 查找x的祖先节点,路径压缩优化
if (father[x] != x) {
return father[x] = find(father[x]);
}
return x;
}
void unionSet(int x, int y) { // 合并x和y所在的集合
int x_fa = find(x);
int y_fa = find(y);
if (x_fa != y_fa) { // 如果x和y不在同一个集合中,则将y的祖先节点设为x的祖先节点
father[y_fa] = x_fa;
}
}
};
int main() {
int n;
cin >> n;
vector<vector<int>> relations(n - 1, vector<int>(2)); // 存储n-1条关系
for (int i = 0; i < n - 1; i++) { // 输入n-1条关系
cin >> relations[i][0] >> relations[i][1];
}
int min_dp = INT_MAX; // 最小的最大连通块大小
vector<int> city; // 最小的最大连通块所在的城市
for (int i = 1; i <= n; i++) { // 枚举每个城市作为特殊城市
UnionFindSet ufs(n + 1); // 初始化并查集
for (const auto& relation : relations) { // 将与特殊城市相连的边删除
int x = relation[0], y = relation[1];
if (x == i || y == i) continue;
ufs.unionSet(x, y);
}
unordered_map<int, int> count; // 统计每个连通块的大小
for (int f : ufs.father) {
f = ufs.find(f);
count[f]++;
}
int dp = 0; // 最大连通块大小
for (const auto& c : count) {
dp = max(dp, c.second);
}
if (dp < min_dp) { // 如果当前最大连通块大小比之前的最小值还小,则更新最小值和最小值所在的城市
min_dp = dp;
city.clear();
city.push_back(i);
}
else if (dp == min_dp) { // 如果当前最大连通块大小与之前的最小值相等,则将城市加入最小值所在的城市列表
city.push_back(i);
}
}
for (int c : city) { // 输出最小的最大连通块所在的城市
cout << c << " ";
}
cout << endl;
return 0;
}
python
class UnionFindSet:
def __init__(self, n):
self.father = [i for i in range(n)]
def find(self, x):
if self.father[x] != x:
self.father[x] = self.find(self.father[x])
return self.father[x]
def unionSet(self, x, y):
x_fa = self.find(x)
y_fa = self.find(y)
if x_fa != y_fa:
self.father[y_fa] = x_fa
n = int(input())
relations = [list(map(int, input().split())) for _ in range(n - 1)]
min_dp = float('inf')
city = []
for i in range(1, n + 1):
ufs = UnionFindSet(n + 1)
for x, y in relations:
if x == i or y == i:
continue
ufs.unionSet(x, y)
count = {}
for f in ufs.father:
f = ufs.find(f)
count[f] = count.get(f, 0) + 1
dp = max(count.values())
if dp < min_dp:
min_dp = dp
city = [i]
elif dp == min_dp:
city.append(i)
print(*city)