Розбір
Consider a tree with vertices.
A centroid of a tree is a vertex whose removal results in splitting the tree into connected components, each containing no more than vertices.
The task is to find all centroids of the tree.
Let’s consider examples of trees and their centroids (marked in green).
Run a depth-first search starting from vertex , which will calculate the number of vertices in the subtree rooted at vertex and store this value in . For the given examples, we will indicate the corresponding value of next to each vertex . In all examples, the depth-first search starts from vertex .
If vertex has children , then
Vertex is a centroid of the tree if and only if:
for each of its children , it holds that ;
if we remove the subtree rooted at from the tree, the resulting tree contains no more than vertices: ;
For example, in the tree from the right with vertices, vertex is a centroid because:
;
;
;
Example
Let's consider the tree from the example. Vertex is a centroid.
Algorithm realization
We’ll store the graph in the adjacency list .
vector<vector<int> > g;
The dfs function returns the number of vertices in the subtree rooted at vertex and saves this value in .
int dfs(int v, int p = -1) { sub[v] = 1; for (int to : g[v]) if (to != p) sub[v] += dfs(to, v); return sub[v]; }
The centroid function performs a depth-first search, finds the centroids, and stores them in the array.
void centroid(int v, int p = -1) {
Set if vertex is a centroid.
int flag = 1;
Iterate through the vertices , adjacent to . Consider an edge .
for (int to : g[v]) if (to != p) {
If for a child it holds that , then is not a centroid.
if (sub[to] > n / 2) flag = 0;
If for a child it holds that , then the subtree rooted at does not contain centroids. It makes sense to continue the search in the child only if .
if (sub[to] >= n / 2) centroid(to, v); }
The tree without the subtree rooted at contains vertices. If it contains more than vertices, then is not a centroid.
if (n - sub[v] > n / 2) flag = 0;
If vertex satisfies all the conditions of a centroid, add it to the array.
if (flag) centr.push_back(v); }
The main part of the program. Read the input data and initialize the arrays.
scanf("%d", &n); g.resize(n + 1); sub.resize(n + 1);
Read the input graph.
for (i = 0; i < n - 1; i++) { scanf("%d %d", &a, &b); g[a].push_back(b); g[b].push_back(a); }
Run a depth-first search and find the centroids of the tree.
dfs(1); centroid(1);
Print one of the centroids.
printf("%d\n", centr[0]);
Algorithm realization – second solution
Let’s run a depth-first search , that will compute the following values for each vertex :
contains the number of vertices in the subtree with vertex ;
contains the maximum size of a subtree among all subtrees of vertex , including the subtree containing the parent vertex of .
Then, the centroid will be the vertex with the smallest value of . There may be either one or two such vertices in the tree.
Vertex is connected to subtrees, with sizes and respectively. The value contains the maximum size of a subtree.
At the same time, the smallest value in the array is . Therefore, vertex is the centroid.
Let’s consider the labeling of a tree with two centroids.
Two vertices have the smallest value in the array : and .
void dfs(int v, int p = -1) { sub[v] = 1; f[v] = 0; for (int to : g[v]) if (to != p) { dfs(to, v); sub[v] += sub[to]; f[v] = max(f[v], sub[to]); } f[v] = max(f[v], n - sub[v]); if ((root == 0) || (f[v] < f[root])) root = v; }
The main part of the program. Read the input data and initialize the arrays.
scanf("%d", &n); g.resize(n + 1); f.resize(n + 1); sub.resize(n + 1); for (i = 0; i < n - 1; i++) { scanf("%d %d", &a, &b); g[a].push_back(b); g[b].push_back(a); }
Run a depth-first search and print one of the centroids of the tree.
root = 0; dfs(1); printf("%d\n", root);
Python realization
Increase the size of the stack.
import sys sys.setrecursionlimit(300000)
The dfs function returns the number of vertices in the subtree rooted at vertex and saves this value in .
def dfs(v, p = -1): sub[v] = 1 for to in g[v]: if to != p: sub[v] += dfs(to, v) return sub[v]
The centroid function performs a depth-first search, finds the centroids, and stores them in the array.
def find_centroid(v, p = -1):
Set if vertex is a centroid.
flag = True
Iterate through the vertices , adjacent to . Consider an edge .
for to in g[v]: if to == p: continue
If for a child it holds that , then is not a centroid.
if sub[to] > n // 2: flag = False
If for a child it holds that , then the subtree rooted at does not contain centroids. It makes sense to continue the search in the child only if .
if sub[to] >= n // 2: find_centroid(to, v)
The tree without the subtree rooted at contains vertices. If it contains more than vertices, then is not a centroid.
if n - sub[v] > n // 2: flag = False
If vertex satisfies all the conditions of a centroid, add it to the array.
if flag: centr.append(v)
The main part of the program. Read the input data and initialize the arrays.
n = int(input()) g = [[] for _ in range(n + 1)] sub = [0] * (n + 1) centr = []
Read the input graph.
for i in range(n - 1): a, b = map(int, input().split()) g[a].append(b) g[b].append(a)
Run a depth-first search and find the centroids of the tree.
dfs(1) find_centroid(1)
Print one of the centroids.
print(centr[0])
Python realization – second solution
Increase the size of the stack.
import sys sys.setrecursionlimit(200000)
Let’s run a depth-first search , that will compute the following values for each vertex :
contains the number of vertices in the subtree with vertex ;
contains the maximum size of a subtree among all subtrees of vertex , including the subtree containing the parent vertex of .
Then, the centroid will be the vertex with the smallest value of . There may be either one or two such vertices in the tree.
def dfs(v, p=-1): global root sub[v] = 1 f[v] = 0 for to in g[v]: if to != p: dfs(to, v) sub[v] += sub[to] f[v] = max(f[v], sub[to]) f[v] = max(f[v], n - sub[v]) if root == 0 or f[v] < f[root]: root = v
The main part of the program. Read the input data and initialize the arrays.
n = int(input()) g = [[] for _ in range(n + 1)] f = [0] * (n + 1) sub = [0] * (n + 1) for _ in range(n - 1): a, b = map(int, input().split()) g[a].append(b) g[b].append(a)
Run a depth-first search and print one of the centroids of the tree.
root = 0 dfs(1) print(root)