Скачиваний:
37
Добавлен:
30.05.2020
Размер:
3.32 Mб
Скачать

 

 

 

{Поки не знайдено вершини,}

while (v <= n) and not flag do

{для якої існує ребро (и, v),}

begin

 

 

 

U := q [ h e a d ] ;

{визначаємо вершину u, якає «головою» черги.}

if (cfu, v] - f[u, v] > 0) and not (v in st) №еп{Якщо існує резерв потоку}

begin

 

{на ребрі (и, v) і вершина v ще не переглядалася,}

q[tail]

:= v;

 

{то записуємо її у «хвіст» черги;}

St := St + [v];

{запам'ятовуємо її як «побачену» у множині;}

lbl[tail].prev := head; {створюємо для неї мітку: номер вершини-предка,}

 

 

{значення оптимального потоку ребром (и, У);}

lbl[tail].flow := min(lbl[head].flow, c[u, v] - f[u, v]);

inc(tail);

 

{збільшуємо значення «хвоста» черги.}

if v = t then flag := true; {Фіксуємо ознаку досягнення вершини-стоку.}

end;

 

 

 

inc(v)

{Переходимо до наступного елемента v рядка и в масиві С.}

end;

 

 

 

if not flag then inc(head)

 

{Якщо вершина-стік не досягнута,}

end;

 

 

{то збільшуємо «голову» черги.}

 

 

 

{Передаємо в основну програму}

n e w f l o w := lbl[tail - 1].flow;

 

{значення потоку у вершині г.}

end;

Для зміни значень прямих ребер, що визначають їх наси­ ченість, можна скористатися такою процедурою:

procedure flows; begin

 

{Визначаємо порядковий номер вершини f у побудованому}

k := tail - 1;

 

 

 

{доповнюючому шляху.}

while lbl[k].prev О 0 do

{Поки не повернемося до вершини s, для якої}

begin

 

 

 

{немає вершини-предка,}

u := q[lbl[k] . prev]; v := q [ k ] ;

{визначаємо вершини ребра (и, v);}

 

 

 

 

{збільшуємо значення потоку}

inc(f[u, v], Iblftail - 1].flow);

{у ребрі (и, v) на величину потоку у вершині г;}

f[v, u] := - f [ u , v ] ;

{запам'ятовуємо значення для зворотного ребра (v, и);}

k := lbl[k].prev;

{значення номера вершини и стає номером вершини v)

end;

{для перегляду наступного ребра визначеного доповнюючого шляху.}

end;

Для коректної роботи основного блоку програми необхідно зробити такі початкові призначення:

for і := 1 to П do {Задання початкових нульових значень для масивів С і F.} for j := 1 to n do

begin

| c [ i , j ] : = 0 ; f [ i , j ] : = 0 ; end;

for І := 1

to m do

{Читання з вхідного файлу}

begin

 

 

read(f_in, u, v, d);

{інформації про ребра мережі}

c [ u , v] := d;

{і внесення її у масив С.)

end;

 

 

flow := 0;

 

{Початкове значення максимального потоку.}

150

Основний блок програми може виглядати так:

repeat

 

{Початковий стан черги q

q[1] := s; lbl[1].prev := 0; lbl[1].flow := maxint;

{та масиву міток/fo/.}

head := 1; tail := 2;

{Початковий стан «голови» і «хвоста» черги.}

 

{Початковий стан множини розглянутих вершин:}

St := [ s ] ;

{починаємо з вершини-витоку.}

flag := false;

 

 

marking(torrent);

{Перехід до процедури побудови доповнюючого шляху.}

if flag then

{Якщо доповнюючий шлях побудовано, то}

b e g i n

{збільшуємо значення максимального потоку}

inc(flow, torrent);

{на значення потоку доповнюючого шляху.}

flows;

{Перехід до процедури перерахунку значення потоку}

end

{на кожному ребрі побудованого доповнюючого шляху.}

Until not flag;

{Завершення побудови максимального потоку.}

Отримані результати можуть бути виведені такою послідов­

ністю операторів:

 

 

writeln(fout, flow);

{Виведення значення максимального потоку.}

for І := 1 to n do

{Виведення значень елементів масиву F,}

for j := 1 to П do {які мають додатне значення, що відповідає ребрам мережі,}

if f [ i , j] > 0 t h e n Writeln(f_OUt, І, ' ', j, ' ', f [ l , j])!

{ЯКИМИ ПРОХОДИВ ПОТІК.}

Цікавою може виявитися ситуація, коли необхідно побудувати максимальний потік у мережі з кількома витоками і стоками (мал. 96, а). Як бути у цьому разі? Виявляється, проблем немає: до­ сить додати до мережі одну вершину-витік і ребра, які з'єднують її з усіма заданими витоками, і вершину-стік, в яку входитимуть ребра з усіх заданих вершин-стоків. «Вага» нових введених до ме­ режі ребер повинна дорівнювати оо (мал. 96, б). Далі лишається

тільки застосувати вже відомий алгоритм Форда-Фалкерсона.

,15^" Y ^ 8

 

,~

^ Д 5 " ї "8

 

®с

лх;

¥&\

а)

5 ^4D «)

~ ^

Мал. 96

Визначимо оцінку алгоритму Форда-Фалкерсона. За наши­ ми позначеннями п - кількість вершин мережі, am - кількість її ребер. Визначимо спочатку, якою буде оцінка кількості іте­ рацій. Основна суть методу полягає у виключенні насичених

151

ребер. Нехай на кожній ітерації, тобто під час кожного прохо­ дження мережі від вершини-витоку до вершини-стоку, виклю­ чається тільки одне ребро. Тоді таких переглядів буде стільки само, скільки і ребер у мережі. Тому оцінку на цьому етапі мож­ на визначити як 0(т). Чи може виключене ребро знову включи­ тися до перегляду? Так, і це може відбутися у разі, коли в одній з вершин, що йому належать, буде розподілено потік. Оскільки така ситуація може виникнути у кожній з я вершин мережі, то загальна оцінка кількості ітерацій становить 0(пт).

У свою чергу, на кожній ітерації виконується пошук у ши­ рину і перерахунок значень потоку на ребрах, що увійшли до доповнюючого шляху. А це вимагає у найгіршому випадку пе­ регляду всіх ребер, тому потребує 0(т) часу. Звідси загальна оцінка методу визначається як 0(пт2).

Щодо тестування алгоритму Форда-Фалкерсона, то слід зазначити, що при доборі тестів необхідно розглядати як ме­ режі з одним витоком і стоком, так і з кількома. При цьому оцінка роботи алгоритму за часом не зміниться, оскільки кіль­ кість вершин збільшується лише на дві.

Корисним є розгляд як невеликих мереж, так і мереж з до­ статньо великою кількістю вершин і ребер. Однак створення таких тестів вимагає розробки спеціальних програм, які врахо­ вували б усі особливості графів, які можна назвати мережами. Для спрощення задачі по генерації таких тестів можна запро­ понувати варіант з'єднання невеликих мереж в одну послідов­ но одна за одною або паралельно з додаванням вершини-витоку і вершини-стоку. Такі мережі можуть бути цікавими для дослідження і порівняння отриманих результатів. Це дасть змогу провести аналітичний аналіз роботи алгоритму побудови максимального потоку в мережі.

Серед тестів обов'язково повинні бути і такі, що вимагають перерозподілу потоку в одній або кількох вершинах. Такі тести, що описують невеликі мережі, найчастіше створюються вручну, а потім можуть бути скомпоновані повторенням у великі мережі.

Завдання

1.Розробити та реалізувати у вигляді програми алгоритм ви­ значення максимального потоку в заданій мережі.

2.Виконати завдання 1 для мережі з кількістю верпіин N ^ 6,

вякій відсутній перерозподіл потоку з використанням зво­ ротних ребер, визначивши кількість необхідних для одер­ жання результату ітерацій. Результат виконання програми вивести у файл.

3.Виконати завдання 1 для мережі з кількістю вершин N К 6,

вякій існує необхідність перерозподілу потоку з викорис-

152

танням зворотних ребер, визначивши кількість необхідних для одержання результату ітерацій. Результат виконання програми вивести у файл.

4. Виконати завдання 1 для мережі, яка є незв'язним графом, з кількістю вершин N^6, визначивши кількість необхідних для одержання результату ітерацій. Результат виконання програми вивести у файл.

5.Виконати завдання 1 для мережі з кількістю вершин N > 6, в якій відсутній перерозподіл потоку з використанням зворотних ребер, визначивши кількість необхідних для одержання резуль­ тату ітерацій. Результат виконання програми вивести у файл.

6.Виконати завдання 1 для мережі з кількістю вершин N > 6,

вякій існує необхідність перерозподілу потоку з викорис­ танням зворотних ребер, визначивши кількість необхідних для одержання результату ітерацій. Результат виконання програми вивести у файл.

7.Виконати порівняльний аналіз виконання завдань 2-6 щодо ефективності роботи алгоритму Форда-Фалкерсона.

/Запитання для самоконтролю

1.Які графи можна назвати мережами?

2.Якими властивостями визначається потік у мережі?

3.Як формулюється задача про максимальний потік у мережі?

4.Які задачі на графах можна віднести до потокових?

5.На якій методиці базується алгоритм Форда-Фалкерсона?

6.Як формулюється задача побудови максимального потоку в ме­ режі у термінах графів?

7.Що розуміють під розрізом мережі? Поясніть це поняття на конк­ ретному прикладі.

8.Як визначається пропускна спроможність розрізу?

9.Що розуміють під мінімальним розрізом мережі?

10.Як пов'язані між собою мінімальний розріз мережі і максималь­ ний потік у ній?

11.Як можна визначити мінімальний розріз мережі і значення мак­ симального потоку?

12.Яким є метод Форда-Фалкерсона для побудови максимального потоку в мережі?

13.У чому полягає суть ітераційного підходу до побудови алгоритму за методом Форда-Фалкерсона?

14.Які вхідні дані необхідні для виконання алгоритму Форда-Фал­ керсона?

15.Який відомий пошуковий алгоритм використовується в методі Форда-Фалкерсона?

16.Запропонуйте власний приклад мережі і продемонструйте на ньому роботу алгоритму методу Форда-Фалкерсона побудови максимального потоку.

17.У чому полягає ідея методу Форда-Фалкерсона побудови мак­ симального потоку щодо перерозподілу потоку у вершині? Про­ демонструйте це на власному прикладі.

153

18.Які ребра у мережі називаються прямими, а які - зворотними?

19.Якою є ознака завершення алгоритму Форда-Фалкерсона побу­ дови максимального потоку?

20.Сформулюйте алгоритм побудови максимального потоку в ме­ режі, що базується на методі Форда-Фалкерсона.

21. Як виглядає реалізація алгоритму побудови максимального пото­ ку в мережі за методом Форда-Фалкерсона у вигляді програми?

22.Як у програмі реалізується ідея введення зворотних ребер та їх обробка?

23.Як можна визначити максимальний потік у мережі для задачі з кількома витоками і стоками?

Ш24. Якою є оцінка методу Форда-Фалкерсона для побудови макси­ мального потоку в мережі? Обґрунтуйте свою відповідь.

Дводольні графи

Продовжуючи тему графів, не можна залишити поза увагою ще один їх різновид - це дводольні графи.

b Дводольним графом називається такий неорієнтований і • граф, у якому всі вершини можна розбити на дві групи, вер­

шини кожної з яких з'єднані ребрами лише з вершинами протилежної групи.

На малюнку 97, а зображено дводольний граф, до першої групи вершин якого входять вершини 1, 2,3,4, 5, а до другої - 6,7,8,9.

Паросполученням у дводольному графі називається мно­ жина ребер, що не мають спільних вершин. На малюнку 97, б жирними лініями виділено ребра, які утворюють саме таке паросполучення. Причому в даному разі - це є максимальне паросполучення, оскільки більше ребер, що не порушують паросполучення, визначити у заданому графі неможливо.

Максимальним паросполученням називається таке паросполучення, яке містить максимально можливу кількість ребер.

Слід, однак, зазначити, що наведене на малюнку 97, бпаросполучення не єдине. Наприклад, ребро (2,7) можна замінити ребром (5,7) і при цьому ознаки паросполучення не будуть порушені.

 

@ \ . \Г\0

2л*^

Г > 7 )

 

®z/

3)гУ

Із®

а)

^ ®

б) '

—^®

 

3/—

 

®—

Мал. 97

154

Класичною задачею, яка розв'язується за допомогою побу­ дови максимального паросполучення на дводольних графах, є задача про шлюби. Вона відома у такому формулюванні. Нехай є N хлопців та М дівчат, які готові взяти шлюб. Відомо, яким хлопцям які дівчата більше до вподоби, а також, з якими хлоп­ цями які дівчата хотіли б побратися. Необхідно визначити максимально можливу кількість майбутніх весіль.

Побудова максимального паросполучення у дводольному графі

Сформульовану задачу можна звести до побудови максималь­ ного потоку в мережі. Для цього необхідно зробити лише кілька підготовчих дій. По-перше, введемо орієнтацію ребер, спрямував­ ши їх від вершин першої групи до вершин другої. По-друге, наван­ тажимо всі ребра одиничною «вагою». По-третє, введемо верши- ну-витік, з'єднавши її з усіма вершинам першої групи, і вершинустік, з якою з'єднаємо всі вершини другої групи. Всім цим додат­ ковим ребрам також надамо одиничної «ваги» (мал. 98, а).

На малюнку 98, б продемонстровано 4 ітерації, які необхід­ но виконати, щоб побудувати всі можливі доповнюючі шляхи у мережі. У кожній ітерації темним кольором виділено верши­ ни, ребра яких утворюють доповнюючий шлях. Оскільки для побудови максимального паросполучення не можна двічі вико­ ристовувати вершини, які уже увійшли до паросполучення, то будемо запам'ятовувати їх у множині.

Мал. 98

У результаті виконання алгоритму Форда-Фалкерсона для побудованої мережі (мал. 98, а) отримаємо максимальний по­ тік, зображений на малюнку 99, а. Як бачимо, отримано ще

155

Мал. 99

один розв'язок поставленої задачі для заданого графа (1,8), (2,6), (4,9), (5,7) і він також відповідає умові існування макси­ мального паросполучення (мал. 99, б).

Слід зазначити, що будь-який новий доповнюючий шлях, знай­ дений у мережі, буде містити лише три ребра, з яких одне середнє і є новим шуканим ребром максимального паросполучення, що бу­ дується, а перше і останнє - це ті ребра, які введені фіктивно.

До речі, значення максимального потоку в нашому прикладі дорівнює 4. Однак задача про побудову максимального паро­ сполучення не вимагає цього результату, оскільки заданий граф незважений.

Дещо змінюється поняття перерозподілу потоку у вершинах графа і тлумачиться наступним чином. Оскільки ми або вклю­ чаємо ребро до паросполучення, або ж ні, то у випадку тупико­ вої ситуації (у разі спроби включення нового ребра до пароспо­ лучення) необхідно ребро, яке заважає цьому, виключити із розгляду, тобто врахувати відповідне йому зворотне ребро.

При визначенні можливості використання поточного ребра не враховується можливість його донасичення, тобто нас не цікавитиме питання, чи є ще резерв у даного ребра. При побу­ дові максимального паросполучення необхідно буде лише ви­ значати, чи поточне ребро (і, j) ще не увійшло до паросполучен­ ня С[і, j] - F[i, j] > 0. У разі позитивної відповіді його необхідно буде включити до паросполучення, збільшивши поточне зна­ чення на 1 inc(F[i, j]), і перерахувати значення відповідного зворотного ребра F\J, і] := -F[i, j].

Проаналізуємо кілька цікавих фактів, які стосуються особ­ ливостей алгоритму побудови максимального паросполучення.

Чому поточне значення елемента масиву F завжди збіль­ шується на 1? Тому що саме така кількість потоку може при­ йти до стокової вершини у дводольному графі, який складаєть­ ся лише з одиничних ребер.

Уточнимо випадок утворення тупикової ситуації під час по­ будови максимального паросполучення у дводольному графі.

156

Для цього розглянемо конкретний приклад (мал. 100, а). На перших двох ітераціях до паросполучення будуть включені реб­ ра (1,5) і (2,4). При побудові наступного доповнюючого шляху, що проходить через вершину 3, тупик утвориться у вершині 5, оскільки вона вже увійшла до паросполучення (мал. 100, б). Щоб вийти із ситуації, необхідно вилучити ребро (1,5) і замість нього включити ребро (3,5). На наступній ітерації буде знайдено новий доповнюючий шлях, куди увійде ребро (1,6) (мал. 100, в). Таким чином буде побудовано максимальне паросполучення для заданого дводольного графа.

Мал. 100

Тепер можна пояснити, чому при включенні зворотного ребра до паросполучення ми фактично при цьому виключаємо відпо­ відне йому пряме ребро. Це відбувається тому, що при почат­ ковому значенні F[i, /] = -1 після виконання операції inc(-F[i, /']) воно перетвориться на F[i, /] = 0. Тобто зворотне ребро виключи­ ться із розгляду. А після виконання операції F\j, і] := -F[i, j] із розгляду буде виключене і відповідне йому пряме ребро. Таким чином і буде здійснено вихід із тупикової ситуації.

Перейдемо до реалізації описаного алгоритму у вигляді програми і зупинимося на деяких змінних, що використовува­ тимуться у ній і набудуть деяких модифікацій.

Оскільки немає необхідності у зберіганні поточного значен­ ня насиченості кожного ребра мережі, то масивом Ibl можна знехтувати, а в масиві q будемо зберігати як номер поточної вершини, яку дописано у чергу, так і номер її вершини-предка.

Оскільки вершини, які вже увійшли до паросполучення, ви­ бувають із подальшого перегляду, то необхідно ввести ще одну множину для зберігання їх номерів. Отже, множина st_q місти­ тиме номери вершин, які розглядаються при поточному пошу­ ку в ширину, a st - номери вершин, які уже увійшли до резуль­ туючого паросполучення.

Зазначені змінні можуть бути описані так:

с, f: array[0..100, 0..100] of byte; q: array[ 1.. 100] of record

top, prev: byte; end;

st_q, st: set of byte;

157

Враховуючи все вищезазначене, можна запропонувати та­ кий варіант алгоритму побудови максимального паросполучення у вигляді процедури:

procedure matching; begin

while (head < tail) and not flag do begin

v := 0;

{Починається перегляд з фіктивної вершини з номером 0.}

 

 

 

 

{Поки не досягнута фіктивна вершина}

while (v

<= n

+1)

and

not

flag do

{з номером n + 1,}

begin

 

 

 

 

 

 

U := q[head].top;

{фіксується вершина, що є початком поточного ребра.}

if ( c [ u , v] - f [ u , v] > 0) and not (v in st_q) then

{Якщо це ребро ще}

 

 

 

{не розглядалося у поточному перегляді, то}

begin

 

 

 

 

 

{фіксується вершина,}

q[tail].top := v; q[tail].prev := head;

{що є кінцем цього ребра,}

St_q := S t q + [v];

{запам'ятовується ця вершина як «побачена»;}

inc(tail);

 

 

 

{збільшується «хвіст» черги.}

if v= n + 1 then flag := true;

{У разі досягнення вершини n + 1,}

end;

 

 

 

 

 

{цей факт фіксується.}

inc(v)

{Перехід до наступної вершини, яка може бути початком ребра.}

end;

 

 

 

 

 

 

if not flag then inc(head)

{Якщо пошук не завершено, то відбувається}

end;

 

 

{перехід до наступної вершини з «голови» черги.}

end;

Процедура перегляду масиву F з метою корекції його значень може бути такою:

procedure flows; begin

k := tail - 1; {Визначається кінець доповнюючого шляху, що містить нове ребро.}

while q[k].prev О 0 do

 

{Поки існує доповнюючий шлях,}

begin

 

{фіксується початок}

u := q[q[k] . prev] . top; v := q [ k ] . t o p ;

{і кінець поточного ребра,}

inc(f[u,v], 1);f[v, u] : = - f [ u , v]; {наявність нового ребра фіксується у масиві F,}

St := st + [v];

{нова вершина запам'ятовується у множині st,}

k := q[k].prev;

{здійснюється перехід до попередньої вершини}

end;

 

{у доповнюючому шляху.}

St := St - [n + 1 ]

{Вилучається фіктивна вершина з номером п + 1.}

end;

 

 

Тіло основної програми також набуде деяких змін:

 

{Підготовка масивів С і Fз урахуванням}

for І := 0 to П + 1 do

 

{фіктивних вершин і ребер.}

for j := 0 to n + 1 do

 

 

begin

 

 

| c [ i , j ] : = 0 ; f [ i , j ] : = 0 ; end;

158

for І := 1 to ID do

{Перегляд ребер мережі.}

begin

 

 

read(f_in, U, v); C[U, v] := 1;

{Введення інформації про ребра мережі.}

с [ 0 , U] := 1; c[v, n + 1] := 1;

{Введення фіктивних ребер.}

end;

 

 

St := [ ] ;

{Підготовка множини для фіксації вершин, що належать ребрам}

repeat

 

{максимального паросполучення.}

q [ 1 ] . t o p := 0; q[1].prev := 0;

{Перший елемент черги - фіктивна вершина 0,}

head := 1; tail := 2;

{що не має предка.}

St_q :== [ 0 ] ;

{Фіксація першої вершини доповнюючого шляху у множині.}

flag := false;

 

 

marking;

{Звернення до процедури визначення доповнюючого шляху.}

if flag then flows;

{Якщо доповнюючий шлях знайдено, то перехід}

until not flag;

 

{до перегляду масиву F.}

Оскільки нас не цікавлять ребра, що виходять із фіктивної вершини 0 і входять до фіктивної вершини п + 1, то виведення отриманого результату буде таким:

for І := 1 to П do

{Виведення номерів вершин,}

for j := 1 to n do

{що належать ребрам}

'f f [ i . j] > 0 then writeln(f_OUt, І, ' ', j);

{максимального паросполучення.}

Аналіз оцінки ефективності роботи алгоритму побудови мак­ симального паросполучення у дводольному графі із застосуван­ ням алгоритму побудови максимального потоку є таким. Оскіль­ ки у даному випадку відсутній варіант перерозподілу потоку у вершинах, то оцінка кількості ітерацій буде залежати тільки від кількості ребер, які по одному будуть виключатися із дводольно­ го графа і включатися до максимального паросполучення. Однак таких ребер буде не т, оскільки далеко не всі ребра входять до шуканого максимального паросполучення. Кількість ребер у максимальному паросполученні дорівнює кількості вершин у меншій з двох груп вершин дводольного графа. Тому і поточна оцінка кількості ітерацій залежить тільки від кількості вершин, а саме 0(п). Оскільки оцінка пошуку в ширину на кожній іте­ рації залишається тією самою, що і в алгоритмі Форда-Фалкер- сона 0(т), то загальна оцінка методу становитиме 0(пт).

Стосовно тестування алгоритму побудови максимального паросполучення слід зазначити, що обов'язковим є підбір тес­ тів як малих, так і середніх та великих. При цьому необхідно передбачити три варіанти: кількість вершин в обох групах приблизно однакова; кількість вершин у групі, що з'єднана з фіктивною вершиною-витоком, значно більша за кількість вер­ шин у групі, що з'єднана з вершиною-стоком; кількість вер­ шин у групі, що з'єднана з фіктивною вершиною-витоком, значно менша за кількість вершин у групі, що з'єднана з вер­ шиною-стоком .

159

Соседние файлы в папке Методи побудови алгоритмів та їх аналіз