자료구조와 알고리즘/Graph

Floyd Warshall (All Pairs Shortest Path)

그레이트쪼 2016. 9. 3. 21:53

Problem definition

  • 그래프에서 모든 노드로부터 모든 노드로의 최단 경로를 구하는 문제
  • Dijkstra를 n번 돌리는 거랑 같지만 이게 더 구현이 간단하다.
  • 문제가 모든 노드로부터 모든 노드까지의 최단 경로 값이기 때문에 결과는 n x n 형태의 테이블이거나 이것들 중에 한 값, 즉 특정 노드에서부터 모든 노드까지 가는 최단 경로의 값이 된다. 
  • 풀이법은 DP이다. 

Input:

       graph[][] = { {0,   5,  INF, 10},

                    {INF,  0,  3,  INF},

                    {INF, INF, 0,   1},

                    {INF, INF, INF, 0} }

which represents the following graph

             10

       (0)------->(3)

        |         /|\

      5 |          |

        |          | 1

       \|/         |

       (1)------->(2)

            3      

Output:

Shortest distance matrix

      0      5      8      9

    INF      0      3      4

    INF    INF      0      1

    INF    INF    INF      0


  • 두 정점을 잇는 최소 비용 경로는 아래의 두 경우 중 최소이다.
    • 경유지를 거치거나
    • 거치지 않는 경우
  • 만일 경유지를 거친다면 그것을 이루는 부분 경로 역시 최단 경로이다.


Dijk = min(Dikk-1 + Dkjk-1, Dijk-1)

  • Dijk = 점 {1, 2, …, k}만을 경유 가능한 점들로 고려하여, 점 i로부터 점 j까지의 모든 경로 중에서 가장 짧은 경로의 거리를 의미한다. 즉, 1 ~ k까지의 어떤 점을 지나도 상관없으며 심지어 i에서 j로 직접 가는 경로도 무방하다.
  • k≠i, k≠j, k=0인 경우에는 어떤 점도 경유하지 않는다는 의미에서 Dij0 는 선분 (i, j)의 거리를 의미한다.
  • Dij1 는 점 1을 경유하여 j로 가는 경로와 i에서 j로 직접 가는 경로, 즉 선분 (i, j) 중에서 짧은 거리이다.
  • Dij2 는 점 2을 경유하여 j로 가는 경로와 점 1까지만 후보로 포함시킨 최소 경로로 j로 가는 경로 중에서 짧은 거리이다. 즉 min(Di21 + D2j1, Dij1)가 되겠다.
  • 이를 일반화 하면 Dijk = min(Dikk-1 + Dkjk-1, Dijk-1) 이다.


Code - Dynamic Programming solution (Bottom-up manner)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// Number of vertices in the graph
#define V 4
  
/* Define Infinite as a large enough value. This value will be used
  for vertices not connected to each other */
#define INF 99999
  
// A function to print the solution matrix
void printSolution(int dist[][V]);
  
// Solves the all-pairs shortest path problem using Floyd Warshall algorithm
void floydWarshall (int graph[][V])
{
    /* dist[][] will be the output matrix that will finally have the shortest 
      distances between every pair of vertices */
    int dist[V][V], i, j, k;
  
    /* Initialize the solution matrix same as input graph matrix. Or 
       we can say the initial values of shortest distances are based
       on shortest paths considering no intermediate vertex. */
    for (i = 0; i < V; i++)
        for (j = 0; j < V; j++)
            dist[i][j] = graph[i][j];
  
    /* Add all vertices one by one to the set of intermediate vertices.
      ---> Before start of a iteration, we have shortest distances between all
      pairs of vertices such that the shortest distances consider only the
      Vertices in set {0, 1, 2, .. k-1} as intermediate vertices.
      ----> After the end of a iteration, vertex no. k is added to the set of
      intermediate vertices and the set becomes {0, 1, 2, .. k} */
    for (k = 0; k < V; k++)
    {
        // Pick all vertices as source one by one
        for (i = 0; i < V; i++)
        {
            // Pick all vertices as destination for the
            // above picked source
            for (j = 0; j < V; j++)
            {
                // If vertex k is on the shortest path from
                // i to j, then update the value of dist[i][j]
                if (dist[i][k] + dist[k][j] < dist[i][j])
                    dist[i][j] = dist[i][k] + dist[k][j];
            }
        }
    }
  
    // Print the shortest distance matrix
    printSolution(dist);
}
  
/* A utility function to print solution */
void printSolution(int dist[][V])
{
    printf ("Following matrix shows the shortest distances"
            " between every pair of vertices \n");
    for (int i = 0; i < V; i++)
    {
        for (int j = 0; j < V; j++)
        {
            if (dist[i][j] == INF)
                printf("%7s""INF");
            else
                printf ("%7d", dist[i][j]);
        }
        printf("\n");
    }
}
  
// driver program to test above function
int main(int argc, char** argv)
{
    int graph[V][V] = {{0,    5, INF,  10},
                        {INF,   0,   3, INF},
                        {INF, INF,   0,   1},
                        {INF, INF, INF,   0}
                        };
  
    // Print the solution
    floydWarshall(graph);
    return 0;
}


Bottom-up 방식을 table로 이해하기