Sözlük sırasına göre en küçük topolojik sıralama, öncelik kuyruklu Kahn algoritması ile oluşturulur: her adımda tüm erişilebilir düğümler (henüz yazılmamış düğümlerden gelen hiçbir kenarı olmayan düğümler) arasından en küçük numaralı düğüm seçilir.
Eklenen bir u \to v kenarı, düğümü yazılana kadar düğümünü erişilemez yapar. Dolayısıyla kenar ekleyerek küçük düğümleri "gizleyebilir" ve böylece algoritmayı daha büyük numaralı düğümleri seçmeye zorlayabiliriz. Gizlenen her düğüm, bütçesinden bir kenara mal olur.
Açgözlü fikir. Cevabı pozisyon pozisyon oluştururuz. Sözlük sırasına göre karşılaştırma, ilk farklı pozisyon tarafından belirlenir; bu yüzden her pozisyona mümkün olan en büyük numaralı düğümü yerleştirmeliyiz — sonraki pozisyonlardaki hiçbir kazanç, mevcut pozisyondaki bir kaybı telafi edemez.
Mevcut adımda yazılabilecek düğümleri iki kümeye ayıralım:
— erişilebilir düğümler: bunlara giren tüm kenarlar zaten "kaldırılmıştır". Hiçbir şey yapmazsak, Kahn algoritması 'nın en küçük düğümünü seçer;
— ertelenmiş düğümler. Bunlar erişilebilir olan, ancak gizlemeye karar verdiğimiz düğümlerdir. Bu tür her düğüm için ona giren bir kenar eklemeyi taahhüt ederiz. Bu kenarın başlangıcını daha sonra seçeriz — bu, ertelenmiş düğümden hemen önce yazılan düğüm olacaktır.
kümesi, en küçük elemanı tepede olan bir öncelik kuyruğunda (min-heap), kümesi ise en büyük elemanı tepede olan bir öncelik kuyruğunda (max-heap) saklanır.
Bir düğümü ertelemek ne zaman kârlıdır. , 'daki en küçük düğüm olsun. Ona dokunmazsak, mevcut pozisyona yerleşir. 'i ertelemek (bir kenar harcamak) iki durumda anlamlıdır:
en az iki düğüm içeriyor. O zaman ertelendikten sonra mevcut pozisyona 'nın artan sıradaki bir sonraki düğümü yerleşir ve bu düğüm 'ten büyüktür;
yalnızca düğümünü içeriyor, ancak ertelenmiş bir y > x düğümü içeriyor. O zaman ertelendikten sonra kümesi boşalır ve mevcut pozisyona en büyük ertelenmiş düğüm — 'ten büyük bir sayı — yerleşir.
Diğer tüm durumlarda erteleme mevcut pozisyondaki sayıyı artırmaz ve kenar harcamaya gerek yoktur. Düğümleri, en küçüğünden başlayarak, kârlı olduğu sürece ve bütçesi tükenmediği sürece birer birer erteleriz.
Hangi düğüm yazılmalı. Ertelemeden sonra:
boş değilse, 'nın en küçük düğümünü yazarız — bu, sözlük sırasına göre en küçük sıralama kuralının gereğidir;
boşsa, 'deki en büyük ertelenmiş düğümünü yazar ve son yazılan düğümden 'ye bir kenar ekleriz. düğümünü "gizleyen" tam olarak bu kenardır: şimdiye kadar erişilemezdi, şimdi ise yeni öncülü yazıldığında tek erişilebilir düğüm hâline gelir. Diğer ertelenmiş düğümler gizli kalır — onların kenarları daha sonra eklenecektir.
Ardından, Kahn algoritmasında her zamanki gibi, yazılan düğümün komşularının giriş derecelerini azaltırız; giriş derecesi sıfır olan düğümler 'ya eklenir.
Cevap neden doğrudur. Her ertelenmiş düğüm er ya da geç 'den yazılır ve o anda tam olarak bir kenar eklenir. Bu nedenle eklenen kenar sayısı erteleme sayısına eşittir ve 'yı aşmaz. Eklenen tüm kenarlar daha önce yazılmış bir düğümden daha sonra yazılmış bir düğüme gider; dolayısıyla bulunan sıra yeni grafın bir topolojik sıralamasıdır ve döngü oluşmaz.
Karmaşıklık. Her düğüm 'ya en fazla bir kez ve 'ye en fazla bir kez girer, grafın her kenarı bir kez incelenir. Toplam karmaşıklık O((n + m) \log n)'dir.
Problemin çözüm algoritması
Kahn algoritmasını ve iki öncelik kuyruğunu kullanırız:
— erişilebilir düğümleri saklayan min-heap, tepesinde en küçük numaralı düğüm bulunur.
— ertelenmiş düğümleri saklayan max-heap, tepesinde en büyük numaralı düğüm bulunur.
Aşağıdaki dizileri tanımlayalım:
indeg[v] — düğümünün henüz işlenmemiş gelen kenarlarının sayısı;
— elde edilen topolojik sıralama;
— eklenen kenarların listesi.
Adım 1. Başlatma
Her düğümün giriş derecesini hesapla.
Giriş derecesi sıfır olan tüm düğümleri 'ya ekle.
'yi ve listesini boş olarak başlat.
Adım 2. Cevabın oluşturulması
Tüm düğüm işlenmediği sürece:
1. Kârlıysa bir düğümü ertele.
Eklenen kenar sayısı 'dan küçük olduğu, yığını boş olmadığı ve aşağıdaki koşullardan en az biri sağlandığı sürece:
en az iki düğüm içeriyor;
tek bir düğümü içeriyor ve , y > x olacak şekilde bir düğümü içeriyor.
'dan en küçük düğümünü çıkar ve 'ye ekle. Bu düğüm daha sonra işlenecektir.
2. Sonraki düğümü seç.
boş değilse, ondan en küçük düğümünü çıkar.
Aksi hâlde 'den en büyük düğümünü çıkar. boş değilse, 'taki son düğümden 'ye bir kenar ekle.
'yi 'ın sonuna ekle.
3. Grafı güncelle.
Her çıkan v \to u kenarı için:
indeg[u]'yu bir azalt.
indeg[u] sıfır olursa, 'yu 'ya ekle.
Tüm düğümler 'a eklenene kadar bu adımları tekrarla.
Adım 3. Çıktı
dizisini yazdır.
Eklenen kenarların sayısını yazdır.
Eklenen kenarların kendilerini yazdır.
Örnek
Problem metnindeki örneği ele alalım: n = 5, k = 2, kenarlar 1 \to 4, 4 \to 2, 1 \to 3. Başlangıç giriş dereceleri: ve düğümlerinin giriş derecesi , düğümlerinin giriş derecesi 'dir. Başlangıç kümeleri A = \{1, 5\}, B = \{\,\}'dir.
Adım | Ertelenen | Alınan | Kenar | Sıra | |||
('da iki düğüm) | 'dan | — | |||||
— | 'den | 5 \to 1 | |||||
('da iki düğüm: ve ) | 'dan | — | |||||
— (k = 0) | 'dan | — | 5, 1, 4, 2 | ||||
— | 'den | 2 \to 3 | 5, 1, 4, 2, 3 |
Adımları daha ayrıntılı inceleyelim.
Adım 1. A = \{1, 5\} iki düğüm içerir, bu yüzden en küçüğünü — 'i — erteleriz. Şimdi A = \{5\} tek bir düğüm içerir ve 'de 'ten büyük düğüm yoktur, bu yüzden ertelemeyi durdururuz. 'i yazarız.
Adım 2. boştur. En büyük ertelenmiş düğüm olan 'i yazar ve 5 \to 1 kenarını ekleriz. 1 \to 4 ve 1 \to 3 kenarlarını kaldırırız: ve düğümleri erişilebilir hâle gelir, A = \{3, 4\}.
Adım 3. iki düğüm içerir — 'ü erteleriz ve kenar bütçesi tükenir (k = 0). 'ü yazarız, ardından 4 \to 2 kenarı kaldırılır ve A = \{2\}.
Adım 4. Başka erteleme mümkün değildir. 'yi yazarız.
Adım 5. boştur. Ertelenmiş düğümünü yazar ve 2 \to 3 kenarını ekleriz.
5, 1, 4, 2, 3 sırasını ve eklenen 5 \to 1 ve 2 \to 3 kenarlarını elde ederiz. Orijinal graf (solda) ve eklenen kenarlarla graf (sağda, eklenen kenarlar kırmızı ile gösterilmiştir):
Kenar eklenmeden sözlük sırasına göre en küçük sıralama 1, 3, 4, 2, 5'tir: düğümü hiçbir şeye bağlı değildir, ancak büyük numarası nedeniyle en son seçilir. İki kenar eklendikten sonra düğümü ilk sıraya geçer ve 5, 1, 4, 2, 3 sırası sözlük sırasına göre daha büyüktür.
Bir örnek daha ele alalım; bu örnekte başlangıçta dört düğüm erişilebilirdir. n = 8, m = 6, k = 4 olsun ve grafın kenarları 6 \to 3, 4 \to 8, 3 \to 5, 1 \to 7, 2 \to 7, 5 \to 7 olsun.
1, 2, 4, 6 düğümlerine hiçbir kenar girmez, bu yüzden başlangıçta A = \{1, 2, 4, 6\}, B = \{\,\}'dir. Kenar eklenmeden sözlük sırasına göre en küçük sıralama 1, 2, 4, 6, 3, 5, 7, 8'dir.
Orijinal graf (solda) ve kenarlar eklendikten sonraki graf (sağda, eklenen kenarlar kırmızı ile gösterilmiştir)
Adım | önce | önce | Ertelenen | Alınan | Kenar | |
\{1, 2, 4, 6\} | 'dan | — | ||||
\{4, 2, 1\} | 3~(4 > 3) | 'den | 6 \to 4 | |||
\{3, 2, 1\} | — | 'dan | — | |||
\{3, 2, 1\} | — | 'den | 8 \to 3 | |||
— | 'dan | — | ||||
— | 'den | 5 \to 2 | ||||
— | 'den | 2 \to 1 | ||||
— | 'dan | — |
Adımları daha ayrıntılı inceleyelim.
Adım 1. A = \{1, 2, 4, 6\}, k = 4. dört düğüm içerdiği için düğümünü, ardından 'yi ('da üç düğüm), sonra 'ü ('da iki düğüm) erteleriz. Bütçe k = 1'e düşer. Şimdi A = \{6\} tek bir düğüm içerir ve en büyük ertelenmiş düğüm , 'dan küçüktür — ertelemeye devam etmek kârlı değildir. 'yı yazarız. 6 \to 3 kenarı kaldırılır ve A = \{3\}.
Adım 2. A = \{3\}, B = \{4, 2, 1\}, k = 1. tek bir düğüm içerir, ancak , 4 > 3 düğümünü içerir, bu yüzden 'ü de erteleriz; bütçe tükenir, k = 0. boştur — en büyük ertelenmiş düğüm olan 'ü yazar ve 6 \to 4 kenarını ekleriz. 4 \to 8 kenarı kaldırılır ve A = \{8\}.
Adım 3. Başka erteleme mümkün değildir. 'i yazarız, ardından boştur.
Adım 4. En büyük ertelenmiş düğüm olan 'ü yazar ve 8 \to 3 kenarını ekleriz. 3 \to 5 kenarı kaldırılır ve A = \{5\}.
Adım 5. 'i yazarız. 5 \to 7 kenarı kaldırılır, ancak düğümüne hâlâ ve 'den kenarlar girer, bu yüzden boştur.
Adım 6. Ertelenmiş düğümünü yazar ve 5 \to 2 kenarını ekleriz. 2 \to 7 kenarı kaldırılır.
Adım 7. Ertelenmiş düğümünü yazar ve 2 \to 1 kenarını ekleriz. 1 \to 7 kenarı kaldırılır ve A = \{7\}.
Adım 8. 'yi yazarız.
Cevap:
6 4 8 3 5 2 1 7
4
6 4
8 3
5 2
2 1Bu örnek algoritmanın tüm durumlarını açıkça gösterir. . adımda tek seferde üç düğüm ertelenir — en az iki düğüm içerdiği sürece. . adımda ikinci koşul geçerli olur: yalnızca düğümünü içerir, ancak daha büyük olan düğümünü içerir, bu yüzden de ertelenir. Ertelenmiş düğümler 'den azalan sırada (4, 3, 2, 1) çıkar ve her biri kendisinden önce yazılan düğümden tam olarak bir kenar alır. Bu nedenle tam olarak erteleme sayısı kadar kenar eklenir — dört.
Algoritmanın gerçekleştirilmesi
Giriş verilerini okuyalım. Grafı oluşturalım. Komşuluk listeleri 'de, giriş dereceleri 'de saklanı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 öncelik kuyruğu tanımlayalım: — erişilebilir düğümler, en küçük eleman tepede (min-heap); — ertelenmiş düğümler, en büyük eleman tepede (max-heap).
priority_queue<int, vector<int>, greater<int>> A; // erisilebilir, min-heap
priority_queue<int> B; // ertelenmis, max-heapBaşlangıçta giriş derecesi sıfır olan tüm düğümler erişilebilirdir.
for (int v = 1; v <= n; v++)
if (indeg[v] == 0) A.push(v);dizisi cevabı — aranan topolojik sıralamayı — biriktirir, dizisi ise eklenen kenarları saklar.
vector<int> order;
order.reserve(n);
vector<pair<int, int>> added;Döngünün her iterasyonu, tüm düğüm yazılana kadar cevaba bir düğüm ekler.
while ((int)order.size() < n)
{Düğümleri erteleriz. Bütçede kenar kaldığı ve kümesi boş olmadığı sürece, en küçük erişilebilir düğümünü ele alırız. en az bir düğüm daha içeriyorsa veya daha büyük numaralı bir ertelenmiş düğüm içeriyorsa onu ertelemek kârlıdır. Ertelenen düğüm 'dan 'ye taşınır ve bütçesi azalır. Aksi hâlde erteleme durur.
// karli oldugu ve kenar kaldigi surece dugumleri erteliyoruz
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;
}Mevcut pozisyona yazılacak düğümünü seçeriz. boş değilse, bu en küçük erişilebilir düğümdür. Aksi hâlde en büyük ertelenmiş düğümü alır ve son yazılan order.back() düğümünden 'ye bir kenar ekleriz.
int v;
if (!A.empty())
{
v = A.top();
A.pop();
}
else
{
v = B.top();
B.pop();
added.push_back(make_pair(order.back(), v));
}'yi cevaba ekleriz ve Kahn algoritmasında olduğu gibi komşularının giriş derecelerini azaltırız. Giriş derecesi sıfır olan düğümler erişilebilir hâle gelir.
order.push_back(v);
for (int to : g[v])
if (--indeg[to] == 0) A.push(to);
}Bulunan topolojik sıralamayı, eklenen kenarların sayısını ve kenarların kendilerini yazdırırız.
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);