Перейти до основного вмісту

Максимальний потік — алгоритм Дініца

Алгоритм Дініца розв'язує задачу про максимальний потік за O(V2E)O(V^2E). Задачу про максимальний потік описано в статті Максимальний потік — Форда-Фалкерсона та Едмондса-Карпа. Цей алгоритм відкрив Юхим Дініц у 1970 році.

Коли підходить цей алгоритм?
  • Задачу можна звести до максимального потоку / мінімального розрізу, а граф великий, щоб виграш від O(V2E)O(V^2E) був відчутним? (якщо граф малий і потрібна найпростіша реалізація → Едмондс-Карп)
  • Це задача про дводольне парування або одиничні пропускні здатності? (на таких мережах Дініц працює особливо швидко — за O(EV)O(E\sqrt{V}))
  • Потрібен саме максимальний потік без вартостей? (якщо ребра мають вартість і потрібен потік мінімальної вартості → Min-cost flow)

Означення

Залишкова мережа GRG^R мережі GG — це мережа, яка містить два ребра для кожного ребра (v,u)G(v, u)\in G:

  • (v,u)(v, u) з пропускною здатністю cvuR=cvufvuc_{vu}^R = c_{vu} - f_{vu}
  • (u,v)(u, v) з пропускною здатністю cuvR=fvuc_{uv}^R = f_{vu}

Блокувальний потік деякої мережі — це такий потік, що кожен шлях зі ss до tt містить принаймні одне ребро, насичене цим потоком. Зауважимо, що блокувальний потік не обов'язково є максимальним.

Шарувата мережа мережі GG — це мережа, побудована таким чином. Спершу для кожної вершини vv ми обчислюємо level[v]level[v] — найкоротший шлях (незважений) зі ss до цієї вершини, що використовує лише ребра з додатною пропускною здатністю. Потім ми лишаємо тільки ті ребра (v,u)(v, u), для яких level[v]+1=level[u]level[v] + 1 = level[u]. Очевидно, що ця мережа є ациклічною.

Алгоритм

Алгоритм складається з кількох фаз. На кожній фазі ми будуємо шарувату мережу залишкової мережі GG. Потім ми знаходимо довільний блокувальний потік у шаруватій мережі й додаємо його до поточного потоку.

Доведення коректності

Покажемо, що якщо алгоритм завершується, то він знаходить максимальний потік.

Якщо алгоритм завершився, то він не зміг знайти блокувальний потік у шаруватій мережі. Це означає, що шарувата мережа не має жодного шляху зі ss до tt. Це означає, що залишкова мережа не має жодного шляху зі ss до tt. Це означає, що потік є максимальним.

Кількість фаз

Алгоритм завершується менш ніж за VV фаз. Щоб це довести, ми спершу маємо довести дві леми.

Лема 1. Відстані зі ss до кожної вершини не зменшуються після кожної ітерації, тобто leveli+1[v]leveli[v]level_{i+1}[v] \ge level_i[v].

Доведення. Зафіксуймо фазу ii і вершину vv. Розгляньмо будь-який найкоротший шлях PP зі ss до vv у Gi+1RG_{i+1}^R. Довжина PP дорівнює leveli+1[v]level_{i+1}[v]. Зауважимо, що Gi+1RG_{i+1}^R може містити лише ребра з GiRG_i^R та зворотні ребра для ребер з GiRG_i^R. Якщо PP не має зворотних ребер для GiRG_i^R, то leveli+1[v]leveli[v]level_{i+1}[v] \ge level_i[v], бо PP є також шляхом у GiRG_i^R. Тепер припустимо, що PP має принаймні одне зворотне ребро. Нехай перше таке ребро — (u,w)(u, w). Тоді leveli+1[u]leveli[u]level_{i+1}[u] \ge level_i[u] (через перший випадок). Ребро (u,w)(u, w) не належить GiRG_i^R, тож ребро (w,u)(w, u) було задіяне блокувальним потоком на попередній ітерації. Це означає, що leveli[u]=leveli[w]+1level_i[u] = level_i[w] + 1. Також leveli+1[w]=leveli+1[u]+1level_{i+1}[w] = level_{i+1}[u] + 1. З цих двох рівностей і leveli+1[u]leveli[u]level_{i+1}[u] \ge level_i[u] ми отримуємо leveli+1[w]leveli[w]+2level_{i+1}[w] \ge level_i[w] + 2. Тепер ми можемо використати ту саму ідею для решти шляху.

Лема 2. leveli+1[t]>leveli[t]level_{i+1}[t] > level_i[t]

Доведення. З попередньої леми, leveli+1[t]leveli[t]level_{i+1}[t] \ge level_i[t]. Припустимо, що leveli+1[t]=leveli[t]level_{i+1}[t] = level_i[t]. Зауважимо, що Gi+1RG_{i+1}^R може містити лише ребра з GiRG_i^R та зворотні ребра для ребер з GiRG_i^R. Це означає, що в GiRG_i^R є найкоротший шлях, який не був заблокований блокувальним потоком. Це суперечність.

З цих двох лем ми робимо висновок, що фаз менше ніж VV, бо level[t]level[t] зростає, але не може бути більшим за V1V - 1.

Знаходження блокувального потоку

Щоб знайти блокувальний потік на кожній ітерації, ми можемо просто намагатися проштовхувати потік за допомогою DFS зі ss до tt у шаруватій мережі, поки його можна проштовхувати. Щоб робити це швидше, ми маємо вилучати ребра, які вже не можна використати для проштовхування. Для цього ми можемо зберігати в кожній вершині вказівник, який вказує на наступне ребро, яке можна використати.

Один запуск DFS займає O(k+V)O(k+V) часу, де kk — кількість просувань вказівника на цьому запуску. Підсумовуючи по всіх запусках, кількість просувань вказівника не може перевищити EE. З іншого боку, загальна кількість запусків не перевищить EE, бо кожен запуск насичує принаймні одне ребро. У такий спосіб загальний час роботи знаходження блокувального потоку становить O(VE)O(VE).

Складність

Фаз менше ніж VV, тож загальна складність дорівнює O(V2E)O(V^2E).

Одиничні мережі

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

На одиничних мережах алгоритм Дініца працює за O(EV)O(E\sqrt{V}). Доведемо це.

По-перше, кожна фаза тепер працює за O(E)O(E), бо кожне ребро буде розглянуте щонайбільше один раз.

По-друге, припустимо, що вже відбулося V\sqrt{V} фаз. Тоді всі доповнювальні шляхи завдовжки V\le\sqrt{V} було знайдено. Нехай ff — поточний потік, ff' — максимальний потік. Розгляньмо їхню різницю fff' - f. Це потік у GRG^R величини ff|f'| - |f|, і на кожному ребрі він дорівнює або 00, або 11. Його можна розкласти на ff|f'| - |f| шляхів зі ss до tt і, можливо, цикли. Оскільки мережа одинична, вони не можуть мати спільних вершин, тож загальна кількість вершин (ff)V\ge (|f'| - |f|)\sqrt{V}, але вона також V\le V, тож ще за V\sqrt{V} ітерацій ми точно знайдемо максимальний потік.

Мережі з одиничними пропускними здатностями

У загальнішому випадку, коли всі ребра мають одиничні пропускні здатності, але кількість вхідних і вихідних ребер не обмежена, шляхи не можуть мати спільних ребер, а не спільних вершин. У схожий спосіб це дозволяє довести оцінку E\sqrt E на кількість ітерацій, тож час роботи алгоритму Дініца на таких мережах становить щонайбільше O(EE)O(E \sqrt E).

Нарешті, також можна довести, що кількість фаз на мережах з одиничними пропускними здатностями не перевищує O(V2/3)O(V^{2/3}), що дає альтернативну оцінку O(EV2/3)O(EV^{2/3}) на мережах з особливо великою кількістю ребер.

Реалізація

struct FlowEdge {
int v, u;
long long cap, flow = 0;
FlowEdge(int v, int u, long long cap) : v(v), u(u), cap(cap) {}
};

struct Dinic {
const long long flow_inf = 1e18;
vector<FlowEdge> edges;
vector<vector<int>> adj;
int n, m = 0;
int s, t;
vector<int> level, ptr;
queue<int> q;

Dinic(int n, int s, int t) : n(n), s(s), t(t) {
adj.resize(n);
level.resize(n);
ptr.resize(n);
}

void add_edge(int v, int u, long long cap) {
edges.emplace_back(v, u, cap);
edges.emplace_back(u, v, 0);
adj[v].push_back(m);
adj[u].push_back(m + 1);
m += 2;
}

bool bfs() {
while (!q.empty()) {
int v = q.front();
q.pop();
for (int id : adj[v]) {
if (edges[id].cap == edges[id].flow)
continue;
if (level[edges[id].u] != -1)
continue;
level[edges[id].u] = level[v] + 1;
q.push(edges[id].u);
}
}
return level[t] != -1;
}

long long dfs(int v, long long pushed) {
if (pushed == 0)
return 0;
if (v == t)
return pushed;
for (int& cid = ptr[v]; cid < (int)adj[v].size(); cid++) {
int id = adj[v][cid];
int u = edges[id].u;
if (level[v] + 1 != level[u])
continue;
long long tr = dfs(u, min(pushed, edges[id].cap - edges[id].flow));
if (tr == 0)
continue;
edges[id].flow += tr;
edges[id ^ 1].flow -= tr;
return tr;
}
return 0;
}

long long flow() {
long long f = 0;
while (true) {
fill(level.begin(), level.end(), -1);
level[s] = 0;
q.push(s);
if (!bfs())
break;
fill(ptr.begin(), ptr.end(), 0);
while (long long pushed = dfs(s, flow_inf)) {
f += pushed;
}
}
return f;
}
};

Задачі для практики

Відеоматеріали