Kinqpinin Qaçışı#9888

Kinqpinin Qaçışı (Həll təhlili)

Sığınacaqlar və yollar təpədən ibarət ağac əmələ gətirir. Yolun bağlanması – kənarın silinməsidir. İstənilən bir kənarı sildikdən sonra qrafın əlaqəli qalması üçün ən az sayda kənar əlavə etmək tələb olunur, yəni onda körpülər olmamalıdır. Əgər qraf əlaqəli qalırsa, onda istənilən sığınacaqdan qərargaha çatmaq mümkündür, buna görə də qərargahın nömrəsi cavaba təsir etmir.

Aşağı qiymətləndirmə. Dərəcəsi olan təpəyə yarpaq deyək və ağacda yarpaq olsun. Yarpaqdan çıxan yeganə kənar körpüdür. Onun körpü olmaması üçün yarpağa ən azı bir yeni kənar çəkmək lazımdır. Hər yeni kənarın iki ucu var, buna görə o, ən çoxu iki yarpağı əhatə edir. Deməli, ən azı yeni kənar lazım olacaq.

Qurma. Göstərək ki, kənar həmişə kifayətdir. İstənilən təpədən dərinliyə axtarış işə salaq və yarpaqları onların gəzilmə sırası ilə yazaq: l_0, l_1, ..., l_{L-1}. k = \lfloor L / 2 \rfloor işarə edək və bu siyahıda nömrələri fərqlənən yarpaqları birləşdirək:

l_0 - l_k, l_1 - l_{k+1}, ..., l_{\lceil L / 2 \rceil - 1} - l_{\lceil L / 2 \rceil - 1 + k}

Dəqiq olaraq kənar alınır və hər yarpaq onlardan ən azı birinin ucu olur. tək olduqda l_k yarpağı iki kənarda iştirak edir.

Misal. Şəkildə L = 5 və k = 2. Yarpaqlar rənglənmişdir, hər birinin yanında gəzilmə sırasındakı nömrəsi göstərilmişdir. Əlavə edilmiş (3, 5), (4, 7) və kənarları nöqtəli xətlə göstərilmişdir, yarpağı onların ikisində iştirak edir.

Niyə körpü qalmır. Ağacı axtarışın işə salındığı təpədən asaq. Ağacın (, -nin valideyni) kənarı körpü olmaqdan çıxır, əgər ən azı bir yeni kənar -nin alt ağacı daxilindəki yarpağı onun xaricindəki yarpaqla birləşdirirsə. Dərinliyə axtarış -nin alt ağacını fasiləsiz olaraq bütövlükdə gəzir, buna görə bu alt ağacın yarpaqları siyahıda ardıcıl gəlir və uzunluqlu parça tutur. Alt ağacda ən azı bir yarpaq var, lakin bütün yarpaq onda ola bilməz: ya kökün öz yarpaqları olan başqa alt ağacları var, ya da kökün özü yarpaqdır. Buna görə 1 \le s \le L - 1.

Yeni kənarda hər yarpağın tərəfdaşı siyahıda ondan dəqiq olaraq mövqe aralı yerləşir. İki hal baxaq:

Hər iki halda alt ağacını qalan ağacla birləşdirən yeni kənar tapılacaq. Deməli, heç bir kənar körpü olaraq qalmayacaq.

Teorem. Ən az yeni kənar sayı -ə bərabərdir. Yarpaqları dərinliyə axtarış sırası ilə yazıb hər l_i yarpağını l_i + \lfloor L / 2 \rfloor yarpağı ilə i = 0, 1, ..., \lceil L / 2 \rceil - 1 üçün birləşdirmək kifayətdir.

n = 2 olduqda yeganə kənarın hər iki ucu yarpaqdır, və cavab onların arasındakı ikinci kənar olacaq.

Misal

Birinci misalda təpəsindən axtarış yarpaqları sırası ilə gəzir. Burada L = 3, k = 1, və proqram və kənarlarını əlavə edir:

Cavab şərtdə verilmiş cavabdan fərqlənir, lakin o da düzgündür: istənilən düzgün iki kənar dəsti uyğun gəlir.

İkinci misalda yarpaqlar gəzilmə sırası ilə: 4, 5, 2, 3. Burada L = 4, k = 2:

0

1

2

3

l_i

4

5

2

3

l_0 - l_2 və l_1 - l_3 birləşdiririk, yəni və kənarlarını əlavə edirik:

Məsələn, kənarını yoxlayaq. təpəsinin alt ağacının yarpaqları və -dir, onlar siyahıda və mövqelərindən ibarət parçanı tuturlar. kənarı bu alt ağacdan çıxır, buna görə kənarı körpü deyil.

Alqoritmin realizasiyası

Ağacı qonşuluq siyahısı şəklində saxlayırıq. Dərinliyə axtarış sırası ilə yarpaqları massivinə toplayırıq.

vector<vector<int>> g;
vector<int> leaves;

dfs funksiyası təpəsindən dərinliyə axtarış aparır. parametri təpəsinin valideynidir, kök üçün p = -1. Əvvəlcə təpəsinin bütün övladlarını gəzirik.

void dfs(int v, int p = -1)
{
  for (int to : g[v])
    if (to != p) dfs(to, v);

Dərəcəsi olan təpə yarpaqdır, onu massivinə yazırıq. İstənilən alt ağacın yarpaqları massivdə ardıcıl düşür.

  if (g[v].size() == 1)
    leaves.push_back(v);
}

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq. Qərargahın nömrəsi cavaba təsir etmir.

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

təpəsindən dərinliyə axtarış işə salırıq və yarpaqları toplayırıq. dəyişəni yarpaqların sayını saxlayır, r = \lceil m / 2 \rceil isə yeni yolların sayıdır.

  dfs(0);
  m = leaves.size();
  r = (m + 1) / 2;

Cavabı çıxarırıq: siyahıda nömrəli yarpağı i + \lfloor m / 2 \rfloor nömrəli yarpaqla birləşdiririk.

  printf("%d\n", r);
  for (i = 0; i < r; i++)
    printf("%d %d\n", leaves[i], leaves[i + m / 2]);