Схема алгоритма Диница — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Определение слоистой сети)
Строка 11: Строка 11:
 
#Для каждого ребра <tex>(u,v)</tex> данной сети <tex>G</tex> зададим <tex>f(u,v) = 0</tex>.
 
#Для каждого ребра <tex>(u,v)</tex> данной сети <tex>G</tex> зададим <tex>f(u,v) = 0</tex>.
 
#Построим вспомогательную сеть <tex>G_L</tex> из [[Дополняющая сеть, дополняющий путь|дополняющей сети]] <tex>G_f</tex> данного графа <tex>G</tex>. Если <tex>d[t] = \infty</tex>, остановиться и вывести <tex>f</tex>.
 
#Построим вспомогательную сеть <tex>G_L</tex> из [[Дополняющая сеть, дополняющий путь|дополняющей сети]] <tex>G_f</tex> данного графа <tex>G</tex>. Если <tex>d[t] = \infty</tex>, остановиться и вывести <tex>f</tex>.
#[[Алгоритм поиска блокирующего потока в ациклической сети|Найдем блокирующий поток <tex>f'</tex> в <tex>G_L</tex>.]].
+
#[[Алгоритм поиска блокирующего потока в ациклической сети|Найдем блокирующий поток <tex>f'</tex> в <tex>G_L</tex>]].
 
#Дополним поток <tex>f</tex> найденным потоком <tex>f'</tex> и перейдем к шагу 2.
 
#Дополним поток <tex>f</tex> найденным потоком <tex>f'</tex> и перейдем к шагу 2.
  
Строка 27: Строка 27:
 
}}
 
}}
 
Поскольку длина кратчайшего <tex>s \leadsto t</tex> пути не может превосходить <tex>n - 1</tex>, то, следовательно, алгоритм Диница совершает не более <tex>n - 1</tex> фазы.
 
Поскольку длина кратчайшего <tex>s \leadsto t</tex> пути не может превосходить <tex>n - 1</tex>, то, следовательно, алгоритм Диница совершает не более <tex>n - 1</tex> фазы.
Таким образом, в зависимости от того, каким алгоритмом нахождения блокирующего потока мы пользовались, весь алгоритм Диница может выполняться за <tex>O(VE^2)</tex> или за <tex>O(V^2E)</tex>. Также возможно достичь асимптотики <tex>O(VE\log V)</tex>, если использовать динамические деревья Слетора и Тарьяна.
+
Таким образом, в зависимости от того, каким алгоритмом нахождения блокирующего потока мы пользовались, весь алгоритм Диница может выполняться за <tex>O(VE^2)</tex> или за <tex>O(V^2E)</tex>. Также возможно достичь асимптотики <tex>O(VE\log V)</tex>, если использовать [[Link-Cut_Tree | динамические деревья Слетора и Тарьяна]].
  
 
==Реализация==
 
==Реализация==
  bfs()
+
В данной реализации не строится вспомогательная сеть <tex>G_L</tex>, а вычисляются значения <tex>d[u]</tex> {{---}} кратчайших путей <tex>s \leadsto u</tex>.
      <tex>Q \leftarrow \emptyset</tex>
 
      <tex>Q</tex>.push(<tex>s</tex>)
 
      while <tex>Q \ne \emptyset</tex>
 
        <tex>u \leftarrow Q</tex>.pop
 
        <tex>for (v :</tex> поток из <tex>u</tex> в <tex>v</tex> положителен и <tex>v</tex> не посещена)
 
            <tex>dist[v] \leftarrow dist[u] + 1 </tex>
 
            <tex>Q</tex>.push(<tex>v</tex>)
 
      <tex>Q</tex>.pop()
 
  
  makeGl()
+
<tex>c[u][v]</tex> {{---}} пропускная способность ребра <tex>(uv)</tex>.
      <tex>dist \leftarrow 0</tex>
 
      bfs()
 
  
  algorithmDinica()
+
<tex>f[u][v]</tex> {{---}} поток через ребро <tex>(uv)</tex>.
      <tex>flow \leftarrow 0</tex>
 
      makeGl()
 
      while (<tex>t</tex> достижима)
 
        Ищем блокирующий поток
 
        Меняем поток <tex>f</tex>
 
        makeGl()
 
      вывести поток <tex>f</tex>
 
  
 +
'''bool''' bfs():
 +
  заполняем массив d значениями, равными -1
 +
  Q.push(s)
 +
  '''while''' !Q.isEmpty
 +
    u = Q.pop()
 +
    '''for''' <tex>(uv) \in E(G)</tex>
 +
      '''if''' f[u][v] < c[u][v] and d[v] != -1
 +
        d[v] = d[u] + 1
 +
        Q.push(v)
 +
  '''return''' d[t] != -1
 +
 +
<font color="darkgreen">//поиск блокирующего потока
 +
//v - номер вершины
 +
//cf - минимальная пропускная способность дополняющей сети на пройденном dfs пути</font>
 +
'''int''' dfs(u, cf):
 +
  '''if''' u == t '''or''' cf == 0
 +
    '''return''' cf
 +
  <font color="darkgreen">//p[u] - номер вершины на которой завершился обход для вершины u</font>
 +
  '''for''' v = p[u] '''to''' <tex>|V(G)| - 1</tex>
 +
    '''if''' d[v] == d[u] + 1 <font color="darkgreen">//это условие эквивалентно поиску во вспомогательной слоистой сети</font>
 +
      cfmin = dfs(v, min(cf, c[u][v] - f[u][v]))
 +
      f[u][v] += cfmin
 +
      f[v][u] -= cfmin
 +
      '''return''' cfmin
 +
    p[u]++
 +
  '''return''' 0
 +
 +
'''int''' findMaxFlow():
 +
  maxFlow = 0
 +
  '''while''' bfs() <font color="darkgreen">//пересчитываем d[i], заодно проверяем достижима ли t из s</font>
 +
    заполняем p нулями
 +
    flow = dfs(s, <tex>\infty</tex>)
 +
    '''while''' flow != 0
 +
      maxFlow += flow
 +
      flow = dfs(s, <tex>\infty</tex>)
 +
  '''return''' maxFlow
 
== Источники ==
 
== Источники ==
 
*[http://www.e-maxx.ru/algo/dinic Алгоритм Диница на e-maxx.ru]
 
*[http://www.e-maxx.ru/algo/dinic Алгоритм Диница на e-maxx.ru]

Версия 03:42, 18 декабря 2015

Определение слоистой сети

Для начала определим для каждой вершины [math]v[/math] данной сети [math]G[/math] длину кратчайшего [math]s \leadsto v[/math] пути из истока и обозначим ее [math]d[v][/math] (для этого можно воспользоваться обходом в ширину).
В слоистую сеть включаем только те ребра [math](u,v)[/math] исходной сети, для которых [math]d[u] + 1 = d[v][/math]. Полученная сеть ациклична, и любой [math]s \leadsto t[/math] путь в слоистой сети является кратчайшим путём в исходной, из свойств обхода в ширину.

Слоистая сеть с пятью слоями. s = 0, t = 6


В примере ребра, обозначенные пунктиром, не входят в слоистую сеть.

Слоистую сеть для графа G будем называть вспомогательной сетью.

Алгоритм

Пусть дана сеть. Требуется найти в этой сети поток [math]f(u,v)[/math] из [math]s[/math] в [math]t[/math] максимальной величины.

Схема алгоритма

  1. Для каждого ребра [math](u,v)[/math] данной сети [math]G[/math] зададим [math]f(u,v) = 0[/math].
  2. Построим вспомогательную сеть [math]G_L[/math] из дополняющей сети [math]G_f[/math] данного графа [math]G[/math]. Если [math]d[t] = \infty[/math], остановиться и вывести [math]f[/math].
  3. Найдем блокирующий поток [math]f'[/math] в [math]G_L[/math].
  4. Дополним поток [math]f[/math] найденным потоком [math]f'[/math] и перейдем к шагу 2.

Корректность алгоритма

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

В самом деле, предположим, что в какой-то момент во вспомогательной сети, построенной для остаточной сети, не удалось найти блокирующий поток. Это означает, что сток вообще не достижим во вспомогательной сети из истока. Но поскольку она содержит в себе все кратчайшие пути из истока в остаточной сети, это в свою очередь означает, что в остаточной сети нет пути из истока в сток. Следовательно, применяя теорему Форда-Фалкерсона, получаем, что текущий поток в самом деле максимален.

Асимптотика алгоритма

Теорема:
Расстояние между истоком и стоком строго увеличивается после каждой фазы алгоритма, т.е. [math]d'[t] \gt d[t][/math], где [math]d'[t][/math] — значение, полученное на следующей фазе алгоритма.
Доказательство:
[math]\triangleright[/math]
Проведём доказательство от противного. Пусть длина кратчайшего пути из истока в сток останется неизменной после очередной фазы алгоритма. Вспомогательная сеть строится по остаточной. Из предположения следует, что в остаточной сети будет содержаться только рёбра остаточной сети перед выполнением данной фазы, либо обратные к ним. Из этого получаем, что нашёлся [math]s \leadsto t[/math] путь, который не содержит насыщенных рёбер и имеет ту же длину, что и кратчайший путь. Но этот путь должен был быть «заблокирован» блокирующим потоком, чего не произошло. Получили противоречие. Значит длина изменилась.
[math]\triangleleft[/math]

Поскольку длина кратчайшего [math]s \leadsto t[/math] пути не может превосходить [math]n - 1[/math], то, следовательно, алгоритм Диница совершает не более [math]n - 1[/math] фазы. Таким образом, в зависимости от того, каким алгоритмом нахождения блокирующего потока мы пользовались, весь алгоритм Диница может выполняться за [math]O(VE^2)[/math] или за [math]O(V^2E)[/math]. Также возможно достичь асимптотики [math]O(VE\log V)[/math], если использовать динамические деревья Слетора и Тарьяна.

Реализация

В данной реализации не строится вспомогательная сеть [math]G_L[/math], а вычисляются значения [math]d[u][/math] — кратчайших путей [math]s \leadsto u[/math].

[math]c[u][v][/math] — пропускная способность ребра [math](uv)[/math].

[math]f[u][v][/math] — поток через ребро [math](uv)[/math].

bool bfs():
  заполняем массив d значениями, равными -1
  Q.push(s)
  while !Q.isEmpty
    u = Q.pop()
    for [math](uv) \in E(G)[/math]
      if f[u][v] < c[u][v] and d[v] != -1
        d[v] = d[u] + 1
        Q.push(v)
  return d[t] != -1
//поиск блокирующего потока
//v - номер вершины
//cf - минимальная пропускная способность дополняющей сети на пройденном dfs пути
int dfs(u, cf):
  if u == t or cf == 0
    return cf
  //p[u] - номер вершины на которой завершился обход для вершины u
  for v = p[u] to [math]|V(G)| - 1[/math]
    if d[v] == d[u] + 1 //это условие эквивалентно поиску во вспомогательной слоистой сети
      cfmin = dfs(v, min(cf, c[u][v] - f[u][v]))
      f[u][v] += cfmin
      f[v][u] -= cfmin
      return cfmin
    p[u]++
  return 0
int findMaxFlow():
  maxFlow = 0
  while bfs() //пересчитываем d[i], заодно проверяем достижима ли t из s
    заполняем p нулями
    flow = dfs(s, [math]\infty[/math])
    while flow != 0
      maxFlow += flow
      flow = dfs(s, [math]\infty[/math])
  return maxFlow

Источники