Алгоритм Флойда: различия между версиями

Материал из Олимпиадное программирование в УлГТУ
Перейти к навигации Перейти к поиску
Нет описания правки
Строка 1: Строка 1:
int g[V_CNT][V_CNT], dist[V_CNT][V_CNT], INF = 1 << 30;
Ищем расстояния во взвешенном графе от каждой вершины до всех остальных. Веса могут быть любыми.
 
Идея алгоритма:
* Инициализация. Пусть dist[a][b] — кратчайшее расстояние от a до b, но длиной не более чем одно ребро. Нетрудно понять, что dist — это просто матрица смежности графа (возможно, с небольшими модификациями):
:* Случай простого графа: dist[a][a] = 0, dist[a][b] = весу ребра (a, b); если ребра (a, b) нет, то dist[a][b] = INF;
:* Если есть кратные рёбра, то dist[a][b] = весу минимального ребра между a и b;
:* Если есть петли, то dist[a][a] = min(0, вес минимальной петли из a);
* Шаг 0. Теперь разрешаем путям проходить через вершину 0. Обновляем dist: dist[a][b] = min(dist[a][b], dist[a][0] + dist[0][b]);
* Шаг 1. Теперь разрешаем путям проходить через вершину 1. Обновляем dist: dist[a][b] = min(dist[a][b], dist[a][1] + dist[1][b]);
* Повторяем до шага n - 1.
 
vector<vector<int>> g(n, vector<int>(n));
vector<vector<int>> dist(n, vector<int>(n));
const int INF = 1e9;
   
   
  for (int i = 0; i < V_CNT; i++)
// считаем, что петель и кратных рёбер нет, граф задан матрицей смежности g, g[a][b] == 0 --- признак отсутствия ребра
     for (int j = 0; j < V_CNT; j++)
  for (int a = 0; a < n; a++) {
         dist[i][j] = (g[i][j] ? g[i][j] : INF);
     for (int b = 0; b < n; b++) {
        if (a == b)
            dist[a][b] = 0;
         else
            dist[a][b] = (g[a][b] ? g[a][b] : INF);
    }
}
   
   
  for (int k = 0; k < V_CNT; k++)
  for (int v = 0; v < n; v++) {
     for (int i = 0; i < V_CNT; i++)
     for (int a = 0; a < n; a++) {
         for (int j = 0; j < V_CNT; j++)
         for (int b = 0; b < n; b++) {
             if (dist[i][k] != INF && dist[k][j] != INF && dist[i][j] > dist[i][k] + dist[k][j]);
             if (dist[a][v] == INF || dist[v][b] == INF)
                dist[i][j] = max(dist[i][k] + dist[k][j], -INF);
                continue;
            dist[a][b] = min(dist[a][b], dist[a][k] + dist[k][b]);
            dist[a][b] = min(dist[a][b], -INF);
        }
    }
}
 
На что нужно обратить внимание:
* При инициализации матрицы на главной диагонали должны быть нули (если нет петель отрицательного веса), а там, где рёбер нет — бесконечности;
* При наличии отрицательных рёбер следует избегать присваиваний вида (INF - 1). Для этого, если хотя бы один из фрагментов пути равен INF, нужно делать continue;
* При наличии отрицательных циклов расстояния могут очень быстро уменьшаться и приводить к отрицательным переполнениям. Поэтому нужно ограничивать отрицательные числа снизу.


== Ссылки на задачи ==
== Ссылки на задачи ==
Строка 20: Строка 49:
* [http://e-maxx.ru/algo/floyd_warshall_algorithm e-maxx.ru &mdash; Алгоритм Флойда-Уоршелла]
* [http://e-maxx.ru/algo/floyd_warshall_algorithm e-maxx.ru &mdash; Алгоритм Флойда-Уоршелла]
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BB%D0%BE%D0%B9%D0%B4%D0%B0 neerc.ifmo.ru/wiki &mdash; Алгоритм Флойда]
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BB%D0%BE%D0%B9%D0%B4%D0%B0 neerc.ifmo.ru/wiki &mdash; Алгоритм Флойда]
* [http://brestprog.neocities.org/lections/floyd.html brestprog.neocities.org &mdash; Алгоритм Флойда-Уоршелла]
* [http://brestprog.by/topics/floyd/ brestprog Алгоритм Флойда-Уоршелла]
* [http://algorithmica.org/tg/shortest-paths algorithmica.org — Кратчайшие пути в графе]
* [http://algorithmica.org/tg/shortest-paths algorithmica.org — Кратчайшие пути в графе]
* [http://brilliant.org/wiki/floyd-warshall-algorithm Brilliant.org — Floyd-Warshall Algorithm]
* [http://brilliant.org/wiki/floyd-warshall-algorithm Brilliant.org — Floyd-Warshall Algorithm]

Версия от 16:44, 15 февраля 2020

Ищем расстояния во взвешенном графе от каждой вершины до всех остальных. Веса могут быть любыми.

Идея алгоритма:

  • Инициализация. Пусть dist[a][b] — кратчайшее расстояние от a до b, но длиной не более чем одно ребро. Нетрудно понять, что dist — это просто матрица смежности графа (возможно, с небольшими модификациями):
  • Случай простого графа: dist[a][a] = 0, dist[a][b] = весу ребра (a, b); если ребра (a, b) нет, то dist[a][b] = INF;
  • Если есть кратные рёбра, то dist[a][b] = весу минимального ребра между a и b;
  • Если есть петли, то dist[a][a] = min(0, вес минимальной петли из a);
  • Шаг 0. Теперь разрешаем путям проходить через вершину 0. Обновляем dist: dist[a][b] = min(dist[a][b], dist[a][0] + dist[0][b]);
  • Шаг 1. Теперь разрешаем путям проходить через вершину 1. Обновляем dist: dist[a][b] = min(dist[a][b], dist[a][1] + dist[1][b]);
  • Повторяем до шага n - 1.
vector<vector<int>> g(n, vector<int>(n));
vector<vector<int>> dist(n, vector<int>(n));
const int INF = 1e9;

// считаем, что петель и кратных рёбер нет, граф задан матрицей смежности g, g[a][b] == 0 --- признак отсутствия ребра
for (int a = 0; a < n; a++) {
   for (int b = 0; b < n; b++) {
       if (a == b)
           dist[a][b] = 0;
       else
           dist[a][b] = (g[a][b] ? g[a][b] : INF);
    }
}

for (int v = 0; v < n; v++) {
    for (int a = 0; a < n; a++) {
        for (int b = 0; b < n; b++) {
            if (dist[a][v] == INF || dist[v][b] == INF)
                continue;
            dist[a][b] = min(dist[a][b], dist[a][k] + dist[k][b]);
            dist[a][b] = min(dist[a][b], -INF);
        }
    }
}

На что нужно обратить внимание:

  • При инициализации матрицы на главной диагонали должны быть нули (если нет петель отрицательного веса), а там, где рёбер нет — бесконечности;
  • При наличии отрицательных рёбер следует избегать присваиваний вида (INF - 1). Для этого, если хотя бы один из фрагментов пути равен INF, нужно делать continue;
  • При наличии отрицательных циклов расстояния могут очень быстро уменьшаться и приводить к отрицательным переполнениям. Поэтому нужно ограничивать отрицательные числа снизу.

Ссылки на задачи

Ссылки