Leksikoqrafik ən kiçik topoloji sıralama prioritet növbəsi ilə Kan alqoritmi vasitəsilə qurulur: hər addımda bütün əlçatan təpələr (hələ yazılmamış təpələrdən daxil olan tili olmayan təpələr) arasından ən kiçik nömrəli təpə seçilir.
Əlavə edilmiş u \to v tili təpəsini təpəsi yazılana qədər əlçatmaz edir. Deməli, til əlavə etməklə kiçik təpələri "gizlədə" və bununla da alqoritmi daha böyük nömrəli təpələri seçməyə məcbur edə bilərik. Hər gizlədilmiş təpə ehtiyatından bir tilə başa gəlir.
Acgöz ideya. Cavabı mövqe-mövqe qururuq. Leksikoqrafik müqayisə ilk fərqlənən mövqe ilə müəyyən olunur, ona görə də hər mövqedə mümkün olan ən böyük nömrəli təpəni yerləşdirməliyik — sonrakı mövqelərdəki heç bir qazanc cari mövqedəki itkini kompensasiya edə bilməz.
Cari addımda yazıla bilən təpələri iki çoxluğa bölək:
— əlçatan təpələr: onlara daxil olan bütün tillər artıq "silinib". Heç nə etməsək, Kan alqoritmi -dan ən kiçik təpəni seçəcək;
— təxirə salınmış təpələr. Bunlar əlçatan olmuş, lakin gizlətməyi qərara aldığımız təpələrdir. Hər belə təpə üçün ona daxil olan bir til əlavə etməyi öhdəmizə götürürük. Bu tilin başlanğıcını sonra seçəcəyik — bu, təxirə salınmış təpədən bilavasitə əvvəl yazılan təpə olacaq.
çoxluğu ən kiçik elementi yuxarıda olan prioritet növbəsində (min-yığın), çoxluğu isə ən böyük elementi yuxarıda olan prioritet növbəsində (max-yığın) saxlanılır.
Təpəni nə vaxt təxirə salmaq sərfəlidir. Tutaq ki, — -dakı ən kiçik təpədir. Ona toxunmasaq, cari mövqeyə yerləşəcək. -i təxirə salmağın (bir til xərcləməyin) iki halda mənası var:
-da ən azı iki təpə var. Onda təxirə salındıqdan sonra cari mövqeyə -dakı artan sırada növbəti, -dən böyük təpə yerləşəcək;
-da yalnız təpəsi var, lakin -də təxirə salınmış y > x təpəsi var. Onda təxirə salındıqdan sonra çoxluğu boş olur və cari mövqeyə ən böyük təxirə salınmış təpə — -dən böyük ədəd — yerləşəcək.
Bütün digər hallarda təxirə salmaq cari mövqedəki ədədi artırmır və til xərcləməyə ehtiyac yoxdur. Təpələri bir-bir, ən kiçikdən başlayaraq, sərfəli olduqca və ehtiyatı tükənməyincə təxirə salırıq.
Hansı təpəni yazmaq lazımdır. Təxirə salmadan sonra:
əgər boş deyilsə, -dan ən kiçik təpəni yazırıq — bunu leksikoqrafik ən kiçik sıralama qaydası tələb edir;
əgər boşdursa, -dən ən böyük təxirə salınmış təpəsini yazırıq və sonuncu yazılmış təpədən -yə til əlavə edirik. Məhz bu til təpəsini "gizlədirdi": indiyə qədər o əlçatmaz idi, indi isə onun yeni sələfi yazıldıqdan sonra o, yeganə əlçatan təpəyə çevrilir. Digər təxirə salınmış təpələr gizli qalır — onların tilləri sonra əlavə ediləcək.
Sonra, Kan alqoritmində olduğu kimi, yazılmış təpənin qonşularının giriş dərəcələrini azaldırıq; giriş dərəcəsi sıfıra bərabər olan təpələr -ya əlavə olunur.
Cavab niyə düzgündür. Hər təxirə salınmış təpə gec-tez -dən yazılır və həmin anda düz bir til əlavə olunur. Buna görə əlavə edilmiş tillərin sayı təxirə salmaların sayına bərabərdir və -dan çox deyil. Bütün əlavə edilmiş tillər əvvəl yazılmış təpədən sonra yazılmış təpəyə gedir, ona görə də tapılmış sıra yeni qrafın topoloji sıralamasıdır və heç bir dövr yaranmır.
Mürəkkəblik. Hər təpə -ya ən çox bir dəfə və -yə ən çox bir dəfə daxil olur, qrafın hər tilinə isə bir dəfə baxılır. Ümumi mürəkkəblik O((n + m) \log n)-dir.
Məsələnin həlli alqoritmi
Kan alqoritmindən və iki prioritet növbəsindən istifadə edirik:
— əlçatan təpələri saxlayan min-yığın, yuxarısında ən kiçik nömrəli təpə yerləşir.
— təxirə salınmış təpələri saxlayan max-yığın, yuxarısında ən böyük nömrəli təpə yerləşir.
Aşağıdakı massivləri elan edək:
indeg[v] — təpəsinin hələ emal olunmamış daxil olan tillərinin sayı;
— nəticə topoloji sıralama;
— əlavə edilmiş tillərin siyahısı.
Addım 1. İnisializasiya
Hər təpənin giriş dərəcəsini hesablamaq.
Giriş dərəcəsi sıfır olan bütün təpələri -ya əlavə etmək.
-ni və siyahısını boş olaraq inisializasiya etmək.
Addım 2. Cavabın qurulması
Bütün təpə emal olunmayana qədər:
1. Sərfəlidirsə, təpəni təxirə salmaq.
Əlavə edilmiş tillərin sayı -dan az olduqca, yığını boş olmadıqca və aşağıdakı şərtlərdən ən azı biri ödəndikcə:
-da ən azı iki təpə var;
-da yeganə təpəsi var, -də isə y > x olan təpəsi var.
-dan ən kiçik təpəsini çıxarıb -yə əlavə etmək. Bu təpə sonra emal olunacaq.
2. Növbəti təpəni seçmək.
Əgər boş deyilsə, ondan ən kiçik təpəsini çıxarmaq.
Əks halda -dən ən böyük təpəsini çıxarmaq. Əgər boş deyilsə, -dakı sonuncu təpədən -yə til əlavə etmək.
-ni -ın sonuna əlavə etmək.
3. Qrafı yeniləmək.
Hər çıxan v \to u tili üçün:
indeg[u]-nu bir vahid azaltmaq.
Əgər indeg[u] sıfıra bərabər olarsa, -nu -ya əlavə etmək.
Bütün təpələr -a əlavə olunana qədər bu addımları təkrarlamaq.
Addım 3. Çıxış
ardıcıllığını çap etmək.
Əlavə edilmiş tillərin sayını çap etmək.
Əlavə edilmiş tillərin özlərini çap etmək.
Nümunə
Şərtdəki nümunəyə baxaq: n = 5, k = 2, tillər 1 \to 4, 4 \to 2, 1 \to 3. İlkin giriş dərəcələri: və təpələrinin giriş dərəcəsi , təpələrinin giriş dərəcəsi -dir. İlkin çoxluqlar: A = \{1, 5\}, B = \{\,\}.
Addım | Təxirə salınan | Götürülür | Til | Sıra | |||
(-da iki təpə) | , -dan | — | |||||
— | , -dən | 5 \to 1 | |||||
(-da iki təpə: və ) | , -dan | — | |||||
— (k = 0) | , -dan | — | 5, 1, 4, 2 | ||||
— | , -dən | 2 \to 3 | 5, 1, 4, 2, 3 |
Addımlara daha ətraflı baxaq.
Addım 1. A = \{1, 5\} iki təpədən ibarətdir, ona görə də ən kiçiyini — -i təxirə salırıq. İndi A = \{5\} bir təpədən ibarətdir, -də isə -dən böyük təpə yoxdur, ona görə də təxirə salmanı dayandırırıq. -i yazırıq.
Addım 2. boşdur. Ən böyük təxirə salınmış təpəsini yazırıq və 5 \to 1 tilini əlavə edirik. 1 \to 4 və 1 \to 3 tillərini silirik: və təpələri əlçatan olur, A = \{3, 4\}.
Addım 3. -da iki təpə var — -ü təxirə salırıq və tillərin ehtiyatı tükənir (k = 0). -ü yazırıq, bundan sonra 4 \to 2 tili silinir və A = \{2\}.
Addım 4. Daha təxirə salmaq mümkün deyil. -ni yazırıq.
Addım 5. boşdur. Təxirə salınmış təpəsini yazırıq və 2 \to 3 tilini əlavə edirik.
5, 1, 4, 2, 3 sırasını və əlavə edilmiş 5 \to 1 və 2 \to 3 tillərini alırıq. İlkin qraf (solda) və əlavə edilmiş tillərlə qraf (sağda, əlavə edilmiş tillər qırmızı rənglə göstərilib):
Əlavə tillər olmadan leksikoqrafik ən kiçik sıralama 1, 3, 4, 2, 5-dir: təpəsi heç nə ilə əlaqəli deyil, lakin böyük nömrəsinə görə sonuncu seçilir. İki til əlavə edildikdən sonra təpəsi birinci olur və 5, 1, 4, 2, 3 sırası leksikoqrafik olaraq daha böyükdür.
Daha bir nümunəyə baxaq, burada əvvəlcə dörd təpə əlçatandır. Tutaq ki, n = 8, m = 6, k = 4, qrafın tilləri isə 6 \to 3, 4 \to 8, 3 \to 5, 1 \to 7, 2 \to 7, 5 \to 7-dir.
1, 2, 4, 6 təpələrinə heç bir til daxil olmur, ona görə də əvvəlcə A = \{1, 2, 4, 6\}, B = \{\,\}. Əlavə tillər olmadan leksikoqrafik ən kiçik sıralama 1, 2, 4, 6, 3, 5, 7, 8-dir.
İlkin qraf (solda) və tillər əlavə edildikdən sonrakı qraf (sağda, əlavə edilmiş tillər qırmızı rənglə göstərilib)
Addım | əvvəl | əvvəl | Təxirə salınan | Götürülür | Til | |
\{1, 2, 4, 6\} | , -dan | — | ||||
\{4, 2, 1\} | 3~(4 > 3) | , -dən | 6 \to 4 | |||
\{3, 2, 1\} | — | , -dan | — | |||
\{3, 2, 1\} | — | , -dən | 8 \to 3 | |||
— | , -dan | — | ||||
— | , -dən | 5 \to 2 | ||||
— | , -dən | 2 \to 1 | ||||
— | , -dan | — |
Addımlara daha ətraflı baxaq.
Addım 1. A = \{1, 2, 4, 6\}, k = 4. təpəsini təxirə salırıq, çünki -da dörd təpə var, sonra -ni (-da üç təpə), sonra -ü (-da iki təpə). Ehtiyat k = 1-ə qədər azalır. İndi A = \{6\} bir təpədən ibarətdir, ən böyük təxirə salınmış təpəsi isə -dan kiçikdir — daha təxirə salmaq sərfəli deyil. -nı yazırıq. 6 \to 3 tili silinir və A = \{3\}.
Addım 2. A = \{3\}, B = \{4, 2, 1\}, k = 1. -da bir təpə var, lakin -də 4 > 3 təpəsi var, ona görə də -ü də təxirə salırıq; ehtiyat tükənir, k = 0. boşdur — ən böyük təxirə salınmış təpəsini yazırıq və 6 \to 4 tilini əlavə edirik. 4 \to 8 tili silinir və A = \{8\}.
Addım 3. Daha təxirə salmaq mümkün deyil. -i yazırıq, bundan sonra boşdur.
Addım 4. Ən böyük təxirə salınmış təpəsini yazırıq və 8 \to 3 tilini əlavə edirik. 3 \to 5 tili silinir və A = \{5\}.
Addım 5. -i yazırıq. 5 \to 7 tili silinir, lakin təpəsinə hələ və -dən tillər daxil olur, ona görə də boşdur.
Addım 6. Təxirə salınmış təpəsini yazırıq və 5 \to 2 tilini əlavə edirik. 2 \to 7 tili silinir.
Addım 7. Təxirə salınmış təpəsini yazırıq və 2 \to 1 tilini əlavə edirik. 1 \to 7 tili silinir və A = \{7\}.
Addım 8. -ni yazırıq.
Cavab:
6 4 8 3 5 2 1 7
4
6 4
8 3
5 2
2 1Bu nümunə alqoritmin bütün hallarını aydın göstərir. -ci addımda birdən üç təpə təxirə salınır — -da ən azı iki təpə olduqca. -ci addımda ikinci şərt işləyir: -da yalnız təpəsi var, lakin -də daha böyük təpəsi var, ona görə də də təxirə salınır. Təxirə salınmış təpələr -dən azalan sırada (4, 3, 2, 1) çıxır və onların hər biri özündən əvvəl yazılmış təpədən düz bir til alır. Buna görə də düz təxirə salmaların sayı qədər til əlavə olunur — dörd.
Alqoritmin reallaşdırılması
Giriş verilənlərini oxuyuruq. Qrafı qururuq. Qonşuluq siyahıları -də, giriş dərəcələri -də saxlanılır.
scanf("%d %d %d", &n, &m, &k);
vector<vector<int>> g(n + 1);
vector<int> indeg(n + 1, 0);
for (int i = 0; i < m; i++)
{
scanf("%d %d", &u, &v);
g[u].push_back(v);
indeg[v]++;
}İki prioritet növbəsi elan edək: — əlçatan təpələr, ən kiçik element yuxarıda (min-yığın); — təxirə salınmış təpələr, ən böyük element yuxarıda (max-yığın).
priority_queue<int, vector<int>, greater<int>> A; // əlçatan, min-yığın
priority_queue<int> B; // təxirə salınmış, max-yığınƏvvəlcə giriş dərəcəsi sıfır olan bütün təpələr əlçatandır.
for (int v = 1; v <= n; v++)
if (indeg[v] == 0) A.push(v);massivində cavab — axtarılan topoloji sıralama toplanır, massivində isə əlavə edilmiş tillər saxlanılır.
vector<int> order;
order.reserve(n);
vector<pair<int, int>> added;Dövrün hər iterasiyası cavaba bir təpə əlavə edir, bütün təpə yazılana qədər.
while ((int)order.size() < n)
{Təpələri təxirə salırıq. Ehtiyatda til qaldıqca və çoxluğu boş olmadıqca, ən kiçik əlçatan təpəsinə baxırıq. Əgər -da ən azı daha bir təpə varsa və ya -də daha böyük nömrəli təxirə salınmış təpə varsa, onu təxirə salmaq sərfəlidir. Təxirə salınmış təpə -dan -yə köçürülür, ehtiyatı isə azalır. Əks halda təxirə salma dayanır.
// sərfəli olduqca və til ehtiyatı qaldıqca təpələri təxirə salırıq
while (k > 0 && !A.empty())
{
int x = A.top();
if (A.size() >= 2 || (!B.empty() && B.top() > x))
{
A.pop();
B.push(x);
k--;
}
else break;
}Cari mövqeyə yazılacaq təpəsini seçirik. Əgər boş deyilsə, bu, ən kiçik əlçatan təpədir. Əks halda ən böyük təxirə salınmış təpəni götürürük və sonuncu yazılmış order.back() təpəsindən -yə til əlavə edirik.
int v;
if (!A.empty())
{
v = A.top();
A.pop();
}
else
{
v = B.top();
B.pop();
added.push_back(make_pair(order.back(), v));
}-ni cavaba əlavə edirik və Kan alqoritmində olduğu kimi onun qonşularının giriş dərəcələrini azaldırıq. Giriş dərəcəsi sıfıra bərabər olan təpələr əlçatan olur.
order.push_back(v);
for (int to : g[v])
if (--indeg[to] == 0) A.push(to);
}Tapılmış topoloji sıralamanı, əlavə edilmiş tillərin sayını və tillərin özlərini çap edirik.
for (int i = 0; i < n; i++)
printf("%d ", order[i]);
printf("\n%d\n", added.size());
for (auto &e : added)
printf("%d %d\n", e.first, e.second);