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

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 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.
  
Строка 35: Строка 35:
  
 
<tex>f[u][v]</tex> {{---}} поток через ребро <tex>(uv)</tex>.
 
<tex>f[u][v]</tex> {{---}} поток через ребро <tex>(uv)</tex>.
 +
 +
<tex>p[u]</tex> {{---}} [[Алгоритм_поиска_блокирующего_потока_в_ациклической_сети#.D0.A3.D0.B4.D0.B0.D0.BB.D1.8F.D1.8E.D1.89.D0.B8.D0.B9_.D0.BE.D0.B1.D1.85.D0.BE.D0.B4 | номер первого неудаленного ребра идущего из u]]
  
 
  '''bool''' bfs():
 
  '''bool''' bfs():
   заполняем массив d значениями, равными -1
+
   заполняем массив d значениями, равными <tex>\infty</tex>
 +
  d[s] = 0
 
   Q.push(s)
 
   Q.push(s)
 
   '''while''' !Q.isEmpty
 
   '''while''' !Q.isEmpty
 
     u = Q.pop()
 
     u = Q.pop()
 
     '''for''' <tex>(uv) \in E(G)</tex>
 
     '''for''' <tex>(uv) \in E(G)</tex>
       '''if''' f[u][v] < c[u][v] and d[v] != -1
+
       '''if''' f[u][v] < c[u][v] '''and''' d[v] == <tex>\infty</tex>
 
         d[v] = d[u] + 1
 
         d[v] = d[u] + 1
 
         Q.push(v)
 
         Q.push(v)
   '''return''' d[t] != -1
+
   '''return''' d[t] != <tex>\infty</tex>
  
 
  <font color="darkgreen">//поиск блокирующего потока
 
  <font color="darkgreen">//поиск блокирующего потока
  //v - номер вершины
+
  //u - номер вершины
 
  //cf - минимальная пропускная способность дополняющей сети на пройденном dfs пути</font>
 
  //cf - минимальная пропускная способность дополняющей сети на пройденном dfs пути</font>
 
  '''int''' dfs(u, cf):
 
  '''int''' dfs(u, cf):
 
   '''if''' u == t '''or''' cf == 0
 
   '''if''' u == t '''or''' cf == 0
 
     '''return''' cf
 
     '''return''' cf
  <font color="darkgreen">//p[u] - номер вершины на которой завершился обход для вершины u</font>
 
 
   '''for''' v = p[u] '''to''' <tex>|V(G)| - 1</tex>
 
   '''for''' v = p[u] '''to''' <tex>|V(G)| - 1</tex>
 
     '''if''' d[v] == d[u] + 1 <font color="darkgreen">//это условие эквивалентно поиску во вспомогательной слоистой сети</font>
 
     '''if''' d[v] == d[u] + 1 <font color="darkgreen">//это условие эквивалентно поиску во вспомогательной слоистой сети</font>
 
       cfmin = dfs(v, min(cf, c[u][v] - f[u][v]))
 
       cfmin = dfs(v, min(cf, c[u][v] - f[u][v]))
       f[u][v] += cfmin
+
       '''if''' cfmin != 0
      f[v][u] -= cfmin
+
        f[u][v] += cfmin <font color="darkgreen">//насыщаем ребра по пути dfs</font>
      '''return''' cfmin
+
        f[v][u] -= cfmin
 +
        '''return''' cfmin
 
     p[u]++
 
     p[u]++
 
   '''return''' 0
 
   '''return''' 0

Версия 13:14, 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].

[math]p[u][/math] номер первого неудаленного ребра идущего из u

bool bfs():
  заполняем массив d значениями, равными [math]\infty[/math]
  d[s] = 0
  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] == [math]\infty[/math]
        d[v] = d[u] + 1
        Q.push(v)
  return d[t] != [math]\infty[/math]
//поиск блокирующего потока
//u - номер вершины
//cf - минимальная пропускная способность дополняющей сети на пройденном dfs пути
int dfs(u, cf):
  if u == t or cf == 0
    return cf
  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]))
      if cfmin != 0
        f[u][v] += cfmin //насыщаем ребра по пути dfs
        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

Источники