У задачі потрібно знайти найкоротші відстані між усіма парами вершин. Це класична задача, яка розв'язується алгоритмом Флойда — Уоршелла. Алгоритм ґрунтується на динамічному програмуванні та коректно працює з ребрами від'ємної ваги за умови, що граф не містить циклів від'ємної ваги. Ця умова гарантована в задачі.
Ідея алгоритму. Пронумеруємо вершини від до . Позначимо через d_k[i][j] довжину найкоротшого шляху з вершини до вершини , усі проміжні вершини якого належать множині \{1, 2, \ldots, k\}. При k = 0 проміжних вершин немає, тому d_0[i][j] дорівнює вазі ребра i \to j, тобто збігається з матрицею суміжності.
Розглянемо найкоротший шлях з до , проміжні вершини якого лежать серед \{1, \ldots, k\}. Можливі два випадки:
шлях не проходить через вершину . Тоді його довжина дорівнює d_{k-1}[i][j];
шлях проходить через вершину . Оскільки циклів від'ємної ваги немає, оптимально відвідати вершину рівно один раз. Шлях розбивається на дві частини: від до і від до . Проміжні вершини обох частин лежать серед \{1, \ldots, k - 1\}, тому довжина шляху дорівнює d_{k-1}[i][k] + d_{k-1}[k][j].
Обираючи кращий з двох варіантів, отримуємо рекурентне співвідношення:
Матриця d_n містить потрібні найкоротші відстані, оскільки в ній проміжною може бути будь-яка вершина графа.
Обчислення в одній матриці. Немає потреби зберігати всі матриці d_0, d_1, \ldots, d_n: усі обчислення можна виконувати безпосередньо в матриці суміжності . Справді, на кроці значення g[i][k] та g[k][j] не змінюються, оскільки g[k][k] = 0. Наприклад, g[i][k] = \min(g[i][k], g[i][k] + g[k][k]) = g[i][k]. Тому не має значення, чи вже оновлено інші елементи матриці на поточному кроці.
Важливо, щоб цикл за проміжною вершиною був зовнішнім: на момент обробки вершини усі відстані з проміжними вершинами з \{1, \ldots, k - 1\} мають бути вже обчислені.
Складність. Алгоритм складається з трьох вкладених циклів по ітерацій кожен, тому час його роботи становить O(n^3). Для n \le 100 це близько 10^6 операцій.
Приклад
Граф, наведений у прикладі, зображено нижче. Граф повний, але ребра ваги на малюнку не показані: як ми побачимо далі, жодне з них не належить жодному найкоротшому шляху.
Просимулюємо роботу алгоритму. Початкова матриця збігається з матрицею суміжності:
\boldsymbol{k = 1}. Шляхи з вершини стають коротшими через вершину : g[4][2] = g[4][1] + g[1][2] = 4 + 5 = 9 та g[4][3] = g[4][1] + g[1][3] = 4 + 9 = 13.
\boldsymbol{k = 2}. Через вершину стають коротшими такі шляхи: g[1][3] = g[1][2] + g[2][3] = 5 + 2 = 7, g[1][4] = g[1][2] + g[2][4] = 5 + 8 = 13 та g[4][3] = g[4][2] + g[2][3] = 9 + 2 = 11.
\boldsymbol{k = 3}. Проходження через вершину не скорочує жодного шляху. Наприклад, g[1][3] + g[3][4] = 7 + 7 = 14 > g[1][4] = 13.
\boldsymbol{k = 4}. Через вершину стають коротшими шляхи до вершин та : g[2][1] = g[2][4] + g[4][1] = 8 + 4 = 12, g[3][1] = g[3][4] + g[4][1] = 7 + 4 = 11 та g[3][2] = g[3][4] + g[4][2] = 7 + 9 = 16.
Зверніть увагу на останнє оновлення. Значення g[4][2] = 9 було отримано на кроці k = 1 і відповідає шляху 4 \to 1 \to 2. Тому g[3][2] = 16 відповідає шляху 3 \to 4 \to 1 \to 2, який має дві проміжні вершини.
Остаточна матриця найкоротших відстаней збігається з відповіддю з прикладу:
Реалізація алгоритму
Збережемо матрицю суміжності графа в масиві .
#define MAX 101
int g[MAX][MAX];Функція floyd реалізує алгоритм Флойда — Уоршелла.
void floyd(void)
{
int i, j, k;
for(k = 0; k < n; k++)
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
if (g[i][k] + g[k][j] < g[i][j]) g[i][j] = g[i][k] + g[k][j];
}Основна частина програми. Зчитаємо вхідний граф.
scanf("%d",&n);
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
scanf("%d",&g[i][j]);Запустимо алгоритм Флойда — Уоршелла.
floyd();Виведемо матрицю найкоротших відстаней між усіма парами вершин.
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
printf("%d ",g[i][j]);
printf("\n");
}Реалізація на Java
import java.util.*;
public class Main
{
static int g[][];
static int n;
static void floyd()
{
for(int k = 0; k < n; k++)
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
if (g[i][k] + g[k][j] < g[i][j])
g[i][j] = g[i][k] + g[k][j];
}
public static void main(String[] args)
{
Scanner con = new Scanner(System.in);
n = con.nextInt();
g = new int[n][n];
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
g[i][j] = con.nextInt();
floyd();
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
System.out.print(g[i][j] + " ");
System.out.println();
}
con.close();
}
}Реалізація на Python
Функція floyd реалізує алгоритм Флойда — Уоршелла.
def floyd():
for k in range(n):
for i in range(n):
for j in range(n):
if g[i][k] + g[k][j] < g[i][j]:
g[i][j] = g[i][k] + g[k][j]Основна частина програми. Зчитаємо вхідний граф.
n = int(input())
g = [[] for _ in range(n)]
for i in range(n):
g[i] = list(map(int, input().split()))Запустимо алгоритм Флойда — Уоршелла.
floyd()Виведемо матрицю найкоротших відстаней між усіма парами вершин.
for i in range(n):
print(*g[i])