Mərkəzi tapın#10158

Mərkəzi tapın (Həll təhlili)

təpədən ibarət olan bir ağac mövcuddur.

Ağacın centroidu elə bir təpədir ki, onu sildikdə ağac elə bir birləşmiş komponentlərə bölünür ki, hər bir komponentdə olan təpələrin sayı -dən çox deyil.

Məsələdə bütün ağacın centroidlarını tapmaq lazımdır.

Ağacların nümunələrini və onların centroidlarını (yaşıl rəngdə göstərilən) nəzərdən keçirək.

Dərinlik üzrə axtarış başladırıq, hansı ki, -də təpəsindən olan alt ağacdakı təpələrin sayını hesablayır. Verilən nümunələrdə hər təpə ilə -nin müvafiq dəyərini göstərəcəyik (dərinlik üzrə axtarış bütün nümunələrdə 1-ci təpədən başlayır).

Əgər təpəsinin övladları to_1, to_2, ..., to_k-dırsa, onda

sub_v = 1 + sub_{to_1} + sub_{to_2} + ... + sub_{to_k}

təpəsi ağacın centroidu yalnız və yalnız aşağıda göstərilən halda olur:

Məsələn, sağda təsvir edilmiş n = 5 təpədən ibarət olan ağacda centroid 2-cı təpə olacaq, çünki

Nümunə

Nümunədəki ağaca baxaq. 6-cı təpə centroiddur.

Alqoritmin həyata keçirilməsi

Qrafi qonşuluq siyahısında saxlayarıq.

vector<vector<int>> g;

dfs funksiyası ilə alt ağacda olan təpələrin sayını qaytarır və bu dəyəri -də saxlayır.

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];
}

centroid funksiyası dərinlik üzrə axtarış aparır, centroidları tapır və massivinə yükləyir.

void centroid(int v, int p = -1)
{

Vəhazərin centroid olduğunu yoxlamaq üçün flag = 1 olaraq işarələyək.

  int flag = 1;

ilə qonşu olan təpələri kontrol edirik. v → to kəsməyi nəzərə alırıq.

  for (int to : g[v])
    if (to != p)
    {

Əgər övladı üçün sub[to] > n / 2 doğru olarsa, onda centroid deyil.

      if (sub[to] > n / 2) flag = 0;

Əgər övladı üçün sub[to] < n / 2 doğru olarsa, bu halda kök olan alt ağacda heç bir centroid yoxdur. Sonda axtarışı yalnız sub[to] \ge n / 2 halda davam etmək məntiqli olur.

      if (sub[to] >= n / 2) centroid(to, v);
    }

Ağac n - sub[v] təpəsiz qalıb. Əgər bu, -dən çox təpəyə malikdirsə, centroid deyil.

  if (n - sub[v] > n / 2) flag = 0;

Əgər təpəsi üçün bütün centroid şərtləri doğru olarsa, o massivinə daxil edilir.

  if (flag) centr.push_back(v);
}

Proqramın əsas hissəsi. Massivləri initialize edirik.

scanf("%d", &n);
g.resize(n + 1);
sub.resize(n + 1);

Giriş qrafını oxuyuruq.

for (i = 0; i < n - 1; i++)
{
  scanf("%d %d", &a, &b);
  g[a].push_back(b);
  g[b].push_back(a);
}

Dərinlik üzrə axtarış başladırıq, ağacın centroidlarını axtarırıq.

dfs(1);
centroid(1);

Bir centroid-i çıxışdırıq.

printf("%d\n", centr[0]);

Alqoritmin həyata keçirilməsi — ikinci həll yolu

Hər bir təpə üçün axtarışı dərinlik üzrə başlayır, hansı ki aşağıdakı dəyərləri hesablayır:

O halda centroid, təpəsi olacaq ki, -nin ən kiçik dəyərinə malikdir. Bu ağacda bir və ya iki belə təpə ola bilər.

v = 6 təpəsi və ölçü olan alt ağaclarla bağlıdır. f[6] = 5 dəyəri maksimal alt ağac ölçüsüdür.

Eyni zamanda massivində ən aşağı dəyər f[6] = 5-dir. Buna görə təpəsi centroid kimi təsbit edilmişdir.

İki centroid olan ağacın nişanlamasını nəzərdən keçirin.

İki təpə massivində ən kiçik dəyərə malikdir: f[1] = 3 və f[2] = 3.

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;
}

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq. Massivləri initialize edirik.

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);
}

Dərinlik üzrə axtarış başladırıq, ağacın kök olan bir centroidini çıxışda veririk.

root = 0;
dfs(1);
printf("%d\n", root);

Python realizasiyası

Stack ölçüsünü artırırıq.

import sys
sys.setrecursionlimit(300000)

dfs funksiyası ilə alt ağacda olan təpələrin sayını qaytarır və bu dəyəri -də saxlayır.

def dfs(v, p = -1):
  sub[v] = 1
  for to in g[v]:
    if to != p:
      sub[v] += dfs(to, v)
  return sub[v]

centroid funksiyası dərinlik üzrə axtarış aparır, centroidları tapır və massivinə yükləyir.

def find_centroid(v, p = -1):

Vəhazərin centroid olduğunu yoxlamaq üçün flag = True olaraq işarələyək.

  flag = True

ilə qonşu olan təpələri kontrol edirik. v → to kəsməyi nəzərə alırıq.

  for to in g[v]:
    if to == p: continue

Əgər övladı üçün sub[to] > n / 2 doğru olarsa, centroid deyil.

    if sub[to] > n // 2:
      flag = False

Əgər övladı üçün sub[to] < n / 2 doğru olarsa, bu halda kök olan alt ağacda heç bir centroid yoxdur. Sonda axtarışı yalnız sub[to] \ge n // 2 halda davam etmək məntiqli olur.

    if sub[to] >= n // 2:
      find_centroid(to, v)

Ağac n - sub[v] təpəsiz qalıb. Əgər bu, -dən çox təpəyə malikdirsə, centroid deyil.

  if n - sub[v] > n // 2:
      flag = False

Əgər təpəsi üçün bütün centroid şərtləri doğru olarsa, o massivinə daxil edilir.

  if flag: centr.append(v)

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq. Massivləri initialize edirik.

n = int(input())
g = [[] for _ in range(n + 1)]
sub = [0] * (n + 1)
centr = []

Giriş qrafını oxuyuruq.

for i in range(n - 1):
  a, b = map(int, input().split())
  g[a].append(b)
  g[b].append(a)

Dərinlik üzrə axtarış başladırıq, ağacın centroidlarını axtarırıq.

dfs(1)
find_centroid(1)

Bir centroid-i çıxışdırıq.

print(centr[0])

Python realizasiyası — ikinci həll yolu

Stack ölçüsünü artırırıq.

import sys
sys.setrecursionlimit(200000)

Hər bir təpə üçün axtarışı dərinlik üzrə başlayır, hansı ki aşağıdakı dəyərləri hesablayır:

O halda centroid, təpəsi olacaq ki, -nin ən kiçik dəyərinə malikdir. Bu ağacda bir və ya iki belə təpə ola bilər.

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

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq. Massivləri initialize edirik.

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)

Dərinlik üzrə axtarış başladırıq, ağacın kök olan bir centroidini çıxışda veririk.

root = 0
dfs(1)
print(root)