题目描述
一张地图上有n个城市,城市和城市之间有且只有一条道路相连:要么直接相连,要么通过其它城市中转相连(可中转一次或多次)。城市与城市之间的道路都不会成环。
当切断通往某个城市 i 的所有道路后,地图上将分为多个连通的城市群,设该城市i的聚集度为DPi(Degree of Polymerization),DPi = max(城市群1的城市个数,城市群2的城市个数,…城市群m 的城市个数)。
请找出地图上DP值最小的城市(即找到城市j,使得DPj = min(DP1,DP2 … DPn))
提示:如果有多个城市都满足条件,这些城市都要找出来(可能存在多个解)
提示:DPi的计算,可以理解为已知一棵树,删除某个节点后;生成的多个子树,求解多个子数节点数的问题。
输入描述
每个样例:第一行有一个整数N,表示有N个节点。1 <= N <= 1000。
接下来的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。 |
输入 | 6 1 2 2 3 2 4 3 5 3 6 |
输出 | 2 3 |
说明 | 将通往2或者3的所有路径切断,最大城市群数量是3,其他任意城市切断后,最大城市群数量都比3大,所以输出2 3 |
题目解析
该问题涉及到树结构的分割,给定一棵树,要求删除每个节点后,计算树被分割成的若干个子树中最大的子树大小。目标是找到使得最大子树大小最小的节点。这个最大子树大小被称为该节点的“聚集度”。
步骤解析:
-
树结构理解:
- 树是一种无环的连通图,给定
N
个城市和N-1
条道路构成一棵树。 - 删除一个节点会使树被分成若干个连通的子树。
- 树是一种无环的连通图,给定
-
计算节点的聚集度:
- 对于每个节点,删除它后,计算分割成的所有子树的大小。
- 找到每个节点的最大子树大小,然后选择最小的最大子树大小。
-
算法步骤:
- DFS (深度优先搜索):用于遍历树并计算每个节点删除后的子树大小。
- 子树大小计算:通过 DFS 计算每个节点的子树大小。
- 聚集度计算:删除节点后,计算剩余的子树大小的最大值。
JAVA算法源码
import java.util.*;
public class TreeAnalysis {
static List<Integer>[] graph;
static int[] subtreeSize;
static int N;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
N = sc.nextInt();
graph = new ArrayList[N + 1];
subtreeSize = new int[N + 1];
for (int i = 1; i <= N; i++) {
graph[i] = new ArrayList<>();
}
for (int i = 0; i < N - 1; i++) {
int x = sc.nextInt();
int y = sc.nextInt();
graph[x].add(y);
graph[y].add(x);
}
// Perform DFS to calculate subtree sizes
dfs(1, -1);
int minDegree = Integer.MAX_VALUE;
List<Integer> result = new ArrayList<>();
for (int i = 1; i <= N; i++) {
int maxSubtree = 0;
boolean isRoot = true;
for (int neighbor : graph[i]) {
if (subtreeSize[neighbor] > subtreeSize[i]) {
maxSubtree = Math.max(maxSubtree, subtreeSize[1] - subtreeSize[i]);
} else {
maxSubtree = Math.max(maxSubtree, subtreeSize[neighbor]);
}
}
if (maxSubtree < minDegree) {
minDegree = maxSubtree;
result.clear();
result.add(i);
} else if (maxSubtree == minDegree) {
result.add(i);
}
}
Collections.sort(result);
for (int node : result) {
System.out.print(node + " ");
}
sc.close();
}
// DFS to compute subtree sizes
static void dfs(int node, int parent) {
subtreeSize[node] = 1;
for (int neighbor : graph[node]) {
if (neighbor != parent) {
dfs(neighbor, node);
subtreeSize[node] += subtreeSize[neighbor];
}
}
}
}
代码解释
-
数据结构:
graph
:邻接表表示树的结构。subtreeSize
:记录每个节点的子树大小。
-
DFS计算子树大小:
- 从根节点开始,递归计算每个节点的子树大小。
-
计算每个节点的聚集度:
- 对每个节点,计算删除该节点后形成的各个子树的最大大小。
- 比较这些最大值,找出最小的最大值对应的节点。
-
输出结果:
- 根据计算结果输出聚集度最小的节点,并按升序排列。
这段代码通过 DFS 和简单的遍历操作高效地解决了问题,适用于 N
最大为 1000 的情况。
JS算法源码
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let input = [];
rl.on('line', (line) => {
input.push(line.trim());
});
rl.on('close', () => {
const N = parseInt(input[0]);
const graph = Array.from({ length: N + 1 }, () => []);
const subtreeSize = Array(N + 1).fill(0);
for (let i = 1; i < input.length; i++) {
const [x, y] = input[i].split(' ').map(Number);
graph[x].push(y);
graph[y].push(x);
}
// Perform DFS to calculate subtree sizes
function dfs(node, parent) {
subtreeSize[node] = 1;
for (const neighbor of graph[node]) {
if (neighbor !== parent) {
dfs(neighbor, node);
subtreeSize[node] += subtreeSize[neighbor];
}
}
}
dfs(1, -1);
let minDegree = Infinity;
let result = [];
for (let i = 1; i <= N; i++) {
let maxSubtree = 0;
let isRoot = true;
for (const neighbor of graph[i]) {
if (subtreeSize[neighbor] > subtreeSize[i]) {
maxSubtree = Math.max(maxSubtree, subtreeSize[1] - subtreeSize[i]);
} else {
maxSubtree = Math.max(maxSubtree, subtreeSize[neighbor]);
}
}
if (maxSubtree < minDegree) {
minDegree = maxSubtree;
result = [i];
} else if (maxSubtree === minDegree) {
result.push(i);
}
}
result.sort((a, b) => a - b);
console.log(result.join(' '));
});
代码解释
-
读取输入:
- 使用 Node.js 的
readline
模块读取输入,输入的第一行是节点数量N
,之后的每一行是连接两个节点的边。
- 使用 Node.js 的
-
树结构初始化:
- 使用邻接表
graph
存储树的结构。 subtreeSize
用于存储每个节点的子树大小。
- 使用邻接表
-
DFS计算子树大小:
dfs
函数从根节点开始递归计算每个节点的子树大小。
-
计算每个节点的聚集度:
- 对每个节点,计算删除该节点后形成的各个子树的最大大小。
- 比较这些最大值,找出最小的最大值对应的节点。
-
输出结果:
- 根据计算结果输出聚集度最小的节点,并按升序排列。
此代码适用于在 Node.js 环境中运行,可以处理节点数 N
最多为 1000 的情况。
Pyhton算法源码
import sys
sys.setrecursionlimit(2000)
def dfs(node, parent):
subtree_size[node] = 1
for neighbor in graph[node]:
if neighbor != parent:
dfs(neighbor, node)
subtree_size[node] += subtree_size[neighbor]
def find_min_max_subtree(n):
min_max_subtree = float('inf')
result = []
for i in range(1, n + 1):
max_subtree = 0
for neighbor in graph[i]:
if subtree_size[neighbor] > subtree_size[i]:
max_subtree = max(max_subtree, subtree_size[1] - subtree_size[i])
else:
max_subtree = max(max_subtree, subtree_size[neighbor])
if max_subtree < min_max_subtree:
min_max_subtree = max_subtree
result = [i]
elif max_subtree == min_max_subtree:
result.append(i)
return result
def main():
input = sys.stdin.read().strip().split('\n')
n = int(input[0])
global graph
global subtree_size
graph = [[] for _ in range(n + 1)]
subtree_size = [0] * (n + 1)
for line in input[1:]:
x, y = map(int, line.split())
graph[x].append(y)
graph[y].append(x)
dfs(1, -1)
result = find_min_max_subtree(n)
print(" ".join(map(str, sorted(result))))
if __name__ == "__main__":
main()
- 使用递归函数
dfs
计算每个节点的子树大小。 find_min_max_subtree
函数计算每个节点删除后的最大子树大小,并找出最小的最大值对应的节点。- 读取输入,构建图,计算结果并输出。
C算法源码
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define MAX_N 1000
int graph[MAX_N + 1][MAX_N + 1];
int subtree_size[MAX_N + 1];
int visited[MAX_N + 1];
void dfs(int node, int parent, int n) {
subtree_size[node] = 1;
visited[node] = 1;
for (int i = 1; i <= n; i++) {
if (graph[node][i] && !visited[i]) {
dfs(i, node, n);
subtree_size[node] += subtree_size[i];
}
}
}
void find_min_max_subtree(int n) {
int min_max_subtree = INT_MAX;
int result[MAX_N];
int result_size = 0;
for (int i = 1; i <= n; i++) {
int max_subtree = 0;
for (int j = 1; j <= n; j++) {
if (graph[i][j]) {
if (subtree_size[j] > subtree_size[i]) {
max_subtree = (subtree_size[1] - subtree_size[i] > max_subtree) ? subtree_size[1] - subtree_size[i] : max_subtree;
} else {
max_subtree = (subtree_size[j] > max_subtree) ? subtree_size[j] : max_subtree;
}
}
}
if (max_subtree < min_max_subtree) {
min_max_subtree = max_subtree;
result_size = 0;
result[result_size++] = i;
} else if (max_subtree == min_max_subtree) {
result[result_size++] = i;
}
}
for (int i = 0; i < result_size; i++) {
if (i > 0) printf(" ");
printf("%d", result[i]);
}
printf("\n");
}
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
graph[i][j] = 0;
}
visited[i] = 0;
subtree_size[i] = 0;
}
for (int i = 1; i < n; i++) {
int x, y;
scanf("%d %d", &x, &y);
graph[x][y] = 1;
graph[y][x] = 1;
}
dfs(1, -1, n);
find_min_max_subtree(n);
return 0;
}
- 使用
dfs
函数计算每个节点的子树大小。 find_min_max_subtree
函数计算每个节点删除后的最大子树大小,并找出最小的最大值对应的节点。- 读取输入,构建图,计算结果并输出。