본문 바로가기

IT/Algorithm

다익스트라(dijkstra) 알고리즘

/* 
written by kaspy (kaspyx@gmail.com)
*/ 

 방향이 있는 그래프에서 꼭지점들을 연결하는 비용이 할당되었을때 임의의 꼭지점에서 다른 꼭지점으로 가는 경로들 중에서 비용이 가장 적게 드는 경로, 즉 두 정점 사이의 최단 경로를 찾는 문제이다.


이 알고리즘음의 간선이 없을 경우에만 적용할수 있다. 욕심쟁이 방법(Greedy Method)을 사용하고있다.


다익스트라 알고리즘은 1959년 컴퓨터 공학자 다익스트라(dijkstra)가 고안해 내었다(이걸로 튜링상 탐, 개2득)


- 다익스트라 알고리즘(Dijstra Algorithm)


1. 출발점이 연결된 마디 중에서 가장 가까운 마디를 선택한다.

2. 선택된 마디에 연결된 마디까지의 거리와 그 전의 마디에서 선택되지 않은 마디의 거리중 가까운것을 선택한다.

3. 모든 마디가 선택될 때까지 반복하며 도착점에 갔을때 기억된 값이 최단 거리가된다.


예를들어 아래와 같은 정점과 간선이 있고 v1에서 v7까지의 최단 경로를 구해보자



( v1->v2로 가는 가중치는 4이다, v4에서 v7로 가는 가중치는 3이다. )


이를 배열에 저장하여 표현할수 있다. a[1][2] =4 이런식으로 저장한다, 마찬가지로 a[4][7]= 3, oo는 무한대, 갈수없으므로)


 

1

2

3

4

5

6

7

1

oo

4

2

oo

oo

oo

oo

2

oo

oo

oo

1

1

oo

oo

3

oo

oo

oo

7

oo

3

oo

4

oo

oo

oo

oo

oo

oo

3

5

oo

oo

oo

oo

oo

oo

1

6

oo

oo

oo

oo

oo

oo

5

7

oo

oo

oo

oo

oo

oo

oo


최단 경로를 구하기위해 다익스트라 알고리즘을 좀더 풀어서 설명해보자


1. 시작점의 거리를 0으로 저장한다.

2. 아직 방문하지 않은 정점 중에서 거리가 가장 짧은 정점을 선택한다.

3. 선택된 정점 v는 최단거리가 확정

4. 선택한 정점을 통해 다른 정점까지의 거리가 짧아지는지 계산한다.

5. 모든 정점이 선택될 때까지 2~4를 반복한다.


모든 과정이 끝나있으면 시작 정점에서 모든 정점까지의 최단경로가 나온다


각 정점까지의 최단 경로를 저장하는 배열 dist 그리고 방문한 정점을 표시해주는 visit 배열이 필요하다.

(v1에서 출발하므로 최단경로는 0)


 

1

2

3

4

5

6

7

dist

0

oo

oo

oo

oo

oo

oo


 

1

2

3

4

5

6

7

visit

0

0

0

0

0

0

0


* 정점 x까지의 최단 경로를 dist[x] 라고 정의할때,

v와 이웃한 정점 i를 연결해주는 간선 a[i][v]가 있다면 dist[i]의 거리 + a[i][v]의 가중치가 작은 i에 대해서 dist[v] = dist[i] + a[i][v]가 된다.


v1에서 가장 가까운 경로는 v1 자신이며, v1을 방문했다고 표시한다.


 

1

2

3

4

5

6

7

dist

0

oo

oo

oo

oo

oo

oo

 

 

1

2

3

4

5

6

7

visit

1

0

0

0

0

0

0


v1 에서 v2까지 가장 가까운 경로는 v1에서 v2까지 가는 경로이며 가중치 합은 4이다.

즉 dist[2] = dist[1] + a[1][2] ( dist[1]까지 최단 경로 + 1에서 2로 가는 가중치합)


 

1

2

3

4

5

6

7

dist

0

4

oo

oo

oo

oo

oo

 

 

1

2

3

4

5

6

7

visit

1

1

0

0

0

0

0


v1에서 v3까지 가장 가까운 경로는 v1에서 v3로 가는 경로이며 가중치 합은 2이다.

즉 dist[3] = dist[1] + a[1][3]


 

1

2

3

4

5

6

7

dist

0

4

2

oo

oo

oo

oo

 

 

1

2

3

4

5

6

7

visit

1

1

1

0

0

0

0


 v4까지 가장 가까운 경로는 v1에서 v3로 가는 경로를 통한값이 최소값이며 가중치 합은 2이다.


즉 dist[4] = dsit[2] + a[2][4]


 

1

2

3

4

5

6

7

dist

0

4

2

5

oo

oo

oo

 

 

1

2

3

4

5

6

7

visit

1

1

1

1

0

0

0


이를 반복하면 아래와 같이 시작 정점에서 각 정점까지 최단 경로가 구해진다.


 

1

2

3

4

5

6

7

dist

0

4

2

5

6

5

7

 

 

1

2

3

4

5

6

7

visit

1

1

1

1

1

1

1


c언어로 구현해보자


  1. #include <stdio.h>
  2.  
  3. #define N 100
  4. #define INF 100000
  5.  
  6. int a[N+1][N+1];
  7. int visit[N+1];
  8. int dist[N+1];
  9. int start, end;
  10. int n,m;
  11.  
  12. // input 값 sample
  13. // 첫번째 라인에는 정점의 개수, 그리고 시작 정점, 도착 정점이 입력
  14. // 두번째 라인에는 정점별 간선의 입력받을 가중치의 개수(m)가 입력된다.
  15. // 세번째 라인부터는 정점별 간선의 입력받을 가중치가 m번이 들어온다.
  16.  
  17.  
  18. /*
  19.  
  20. 7 1 7
  21. 9
  22. 1 2 4
  23. 1 3 2
  24. 2 4 1
  25. 2 5 2
  26. 3 4 7
  27. 3 6 3
  28. 4 7 3
  29. 5 7 1
  30. 6 7 5
  31.  
  32. */
  33.  
  34.  
  35. void input()
  36. {
  37.         int i,j;
  38.         int from,to;
  39.         int w;
  40.         scanf("%d %d %d",&n,&start,&end);
  41.         scanf("%d",&m);
  42.  
  43.         // 각 정점으로 가는 간선의 가중치를 무한대로 초기화한다.(최소값을 구하기위해)
  44.         for ( i =1;<=n; i++)
  45.                 for ( j =1; j <=n; j++)
  46.                         if ( i!= j)
  47.                                 a[i][j] = INF;
  48.        
  49.         for ( i =1;<= m; i++) // 정점에서 정점으로 가는 간선의 가중치가 입력
  50.         {
  51.                 scanf("%d %d %d",&from,&to,&w);
  52.                 a[from][to] =w;
  53.         }
  54.  
  55.         for ( i =1;<=n; i++)
  56.                 dist[i] = INF;
  57.                
  58. }
  59.  
  60. void dijkstra()
  61. {
  62.         int i,j;
  63.         int min;
  64.         int v;
  65.  
  66.         dist[start] = 0;        // 시작점의 거리 0
  67.  
  68.         for ( i =1;<=n; i++)
  69.         {
  70.                 min = INF;
  71.                
  72.                 for ( j =1 ; j <=n; j++)
  73.                 {
  74.                         if ( visit[j] == 0 && min > dist[j])    // 갈수 있는 정점중에 가장 가까운 정점 선택
  75.                         {
  76.                                 min = dist[j];
  77.                                 v = j;
  78.                         }
  79.                 }
  80.  
  81.                 visit[v] = 1;   // 가장 가까운 정점으로 방문, i정점에서 가장 가까운 최단경로 v
  82.        
  83.                 for ( j = 1;<= n; j++)      
  84.                 {
  85.                         if ( dist[j] > dist[v] + a[v][j])       // 방문한 정점을 통해 다른 정점까지의 거리가 짧아지는지 계산하여 누적된값 저장
  86.                                 dist[j] = dist[v] + a[v][j];
  87.                 }
  88.         }
  89. }
  90.  
  91.  
  92. int main(void)
  93. {
  94.         input();
  95.         dijkstra();
  96.         printf("%d \n",dist[end]);
  97.         return 0;
  98. }