Floyd - 1#974

Floyd - 1 (Həll təhlili)

Məsələ bütün təpə cütləri arasında ən qısa məsafələrin tapılmasını tələb edir. Bu, Floyd — Warshall alqoritmi ilə həll olunan klassik məsələdir. Alqoritm dinamik proqramlaşdırmaya əsaslanır və qrafda mənfi çəkili tsikl olmadığı şərtlə mənfi kənar çəkiləri ilə düzgün işləyir. Bu şərt məsələdə təmin olunur.

Alqoritmin ideyası. Təpələri -dən -ə qədər nömrələyək. d_k[i][j] ilə təpəsindən təpəsinə qədər olan, bütün aralıq təpələri \{1, 2, \ldots, k\} çoxluğuna aid olan ən qısa yolun uzunluğunu işarə edək. k = 0 üçün aralıq təpələr yoxdur, ona görə d_0[i][j] i \to j kənarının çəkisinə bərabərdir, yəni qonşuluq matrisi ilə üst-üstə düşür.

-dən -yə, aralıq təpələri \{1, \ldots, k\} çoxluğunda yerləşən ən qısa yolu nəzərdən keçirək. İki hal mümkündür:

İki variantdan daha yaxşısını seçərək, aşağıdakı rekurrent münasibəti alırıq:

d_k[i][j] = \min(d_{k-1}[i][j], d_{k-1}[i][k] + d_{k-1}[k][j])

d_n matrisi tələb olunan ən qısa məsafələri ehtiva edir, çünki qrafın istənilən təpəsi onda aralıq təpə ola bilər.

Tək matrisdə hesablama. Bütün d_0, d_1, \ldots, d_n matrislərini saxlamağa ehtiyac yoxdur: bütün hesablamaları birbaşa qonşuluq matrisi -də yerinə yetirmək olar. Həqiqətən, addımında g[i][k] və g[k][j] qiymətləri dəyişmir, çünki g[k][k] = 0. Məsələn, g[i][k] = \min(g[i][k], g[i][k] + g[k][k]) = g[i][k]. Ona görə də cari addımda matrisin digər elementlərinin artıq yenilənib-yenilənməməsinin əhəmiyyəti yoxdur.

Vacibdir ki, aralıq təpə üzrə dövr xarici dövr olsun: təpəsi emal olunana qədər, aralıq təpələri \{1, \ldots, k - 1\} çoxluğundan olan bütün məsafələr artıq hesablanmış olmalıdır.

Mürəkkəblik. Alqoritm hər biri təkrardan ibarət üç iç-içə dövrdən ibarətdir, ona görə işləmə vaxtı O(n^3)-dir. n \le 100 üçün bu təxminən 10^6 əməliyyatdır.

Misal

Misalda verilən qraf aşağıda göstərilib. Qraf tamdır, lakin çəkisi olan kənarlar şəkildə göstərilməyib: aşağıda görəcəyimiz kimi, onlardan heç biri heç bir ən qısa yola daxil olmur.

Alqoritmi simulyasiya edək. İlkin matrisi qonşuluq matrisi ilə üst-üstə düşür:

\boldsymbol{k = 1}. təpəsindən yollar təpəsi vasitəsilə qısalır: g[4][2] = g[4][1] + g[1][2] = 4 + 5 = 9 və g[4][3] = g[4][1] + g[1][3] = 4 + 9 = 13.

\boldsymbol{k = 2}. təpəsi vasitəsilə aşağıdakı yollar qısalır: g[1][3] = g[1][2] + g[2][3] = 5 + 2 = 7, g[1][4] = g[1][2] + g[2][4] = 5 + 8 = 13 və g[4][3] = g[4][2] + g[2][3] = 9 + 2 = 11.

\boldsymbol{k = 3}. təpəsindən keçmək heç bir yolu qısaltmır. Məsələn, g[1][3] + g[3][4] = 7 + 7 = 14 > g[1][4] = 13.

\boldsymbol{k = 4}. təpəsi vasitəsilə və təpələrinə yollar qısalır: g[2][1] = g[2][4] + g[4][1] = 8 + 4 = 12, g[3][1] = g[3][4] + g[4][1] = 7 + 4 = 11 və g[3][2] = g[3][4] + g[4][2] = 7 + 9 = 16.

Son yeniləməyə diqqət edin. g[4][2] = 9 qiyməti k = 1 addımında alınıb və 4 \to 1 \to 2 yoluna uyğun gəlir. Buna görə, g[3][2] = 16 iki aralıq təpəsi olan 3 \to 4 \to 1 \to 2 yoluna uyğun gəlir.

Son ən qısa məsafələr matrisi misaldakı cavabla üst-üstə düşür:

Alqoritmin realizasiyası

Qrafın qonşuluq matrisini massivində saxlayın.

#define MAX 101
int g[MAX][MAX];

floyd funksiyası Floyd — Warshall alqoritmini realizə edir.

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];
}

Proqramın əsas hissəsi. Daxil olan qrafı oxuyuruq.

scanf("%d",&n);
for(i = 0; i <  n; i++)
for(j = 0; j <  n; j++)
  scanf("%d",&g[i][j]);

Floyd — Warshall alqoritmini işə salırıq.

floyd();

Bütün təpə cütləri arasında ən qısa məsafələr matrisini çap edirik.

for(i = 0; i <  n; i++)
{
  for(j = 0; j <  n; j++)
    printf("%d ",g[i][j]);
  printf("\n");
}

Java realizasiyası

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 realizasiyası

floyd funksiyası Floyd — Warshall alqoritmini realizə edir.

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]

Proqramın əsas hissəsi. Daxil olan qrafı oxuyuruq.

n = int(input())
g = [[] for _ in range(n)]
for i in range(n):
  g[i] = list(map(int, input().split()))

Floyd — Warshall alqoritmini işə salırıq.

floyd()

Bütün təpə cütləri arasında ən qısa məsafələr matrisini çap edirik.

for i in range(n):
  print(*g[i])