알고리즘/알고리즘(C,C++)

[그래프]DFS - 오일러 서킷

산을좋아한라쯔 2016. 4. 16. 18:29
반응형

오일러 서킷은, 그래프상의 모든 간선(Edge)을 한 번씩만 지나서 시작점으로 돌아오는 경로를 말한다. 

흔히, 한 붓그리기로 알려져 있는 것이다.

(위키 참조: https://en.wikipedia.org/wiki/Eulerian_path)



위 그림을 보면, 1-2-4-3-4-6-5-3-1 의 경로가 오일러 서킷이 된다.


오일러서킷을 구하는 방법은 다음과 같다. (DFS처럼, 경로를 따라가면서 재귀호출. But, DFS와 똑같진 않다.)

 - getEulerCircuit(u)함수는 u와 인접한 간선들을 모두 검사하면서, 

 - 아직 방문하지 않은 간선(u,v)가 있다면, 연결을 끊고, getEulerCircuit(v)를 호출

 - 더 이상 따라갈 간선이 없다면 종료하는데, 종료하기 직전에 u를 circuit벡터에 보관

 - circuit벡터를 reverse하면 오일러 서킷이 됨.


위 그림의 예를, 오일러서킷 구하는 방법으로 실제 시뮬레이션해보면,

  1) 1번에서 시작.  연결된 2번과 3번중에서, 먼저 2번에 대해서, 1-2연결 끊고, 재귀호출(2)

  2) 2번과 연결된 4번에 대해서, 2-4연결 끊고, 재귀호출(4)

  3) 4번과 연결된 3,6 중에서 3번에 대해, 4-3 연결끊고, 재귀호출(3)

  4) 3번에 대해 연결된 1,4,5번중에서 1번에 대해(가장 먼저 있는 것이기에), 3-1 연결 끊고, 재귀호출(1)

  5) 1번에 대해서 남아있는 연결이 없기에 종료하면서 push(1)  

    여기까지 진행된 경로를 되새겨보면 아래그림과 같다.


6) 재귀호출(1)이 종료되었기에, 이 재귀호출(1)을 호출한 재귀호출(3)이 계속이어서 진행.

   즉 재귀호출(3)에서, 3과 연결된 1,4,5번중에서 먼저 1번에 대해 처리한 것이고, 이제 4번에 대해서 처리

   3-4 연결 끊고, 재귀호출(4) 

7) 4번과 연결된 6번에 대해서, 4-6 연결 끊고, 재귀호출(6)

8) 6번과 연결된 5번에 대해서, 6-5 연결 끊고, 재귀호출(5)

9) 5번과 연결된 3번에 대해서, 5-3 연결 끊고, 재귀호출(3)

10)3번에서 주위를 봐도, 남아 있는 연결이 없기에 종료하면서 push(3)

11)상위 호출인 재귀호출(5)에서도, 남아있는 연결이 없기에, 종료하면서 push(5)

12)마찬가지로 6번에 대해서도 종료하면서 push(6)

13)마찬가지로 4번에 대해서도 종료하면서 push(4)


14)재귀호출(4)를 한 3번점에서 봐도 이제는 남아있는 연결이 없기에, 종료하면서 push(3)

15)마찬가지로 push(4)

16)push(2)

17)push(1)

18)push되어 있는 것을 역으로 보면 1-2-4-3-4-6-5-3-1 로, 오일러 서킷이 되었음을 알 수 있음.


코드로 짜보면 다음과 같다.

#include <stdio.h>

#include <vector>

#include <algorithm>

#include <limits.h>

#include <iostream>

 

using namespace std;

 

vector<vector<int>> adj;

void getEulerCircuit(int u, vector<int>& circuit){

         for (int v = 1; v < adj.size(); ++v){

                  while (adj[u][v] > 0){

                           adj[u][v]--;

                           adj[v][u]--;

                           getEulerCircuit(v, circuit);

                  }

         }

         circuit.push_back(u);

}

 

void printCircuit(vector<int>& circuit){

         reverse(circuit.begin(), circuit.end());

         for (int i = 0; i < circuit.size(); ++i) cout << circuit[i] << " ";   

         cout << endl;

}

 

int main(void){

         freopen("EulerCircuit.txt", "r", stdin);

         setbuf(stdout, NULL);

 

         int C, N, T;

         cin >> C;

 

         for (int test = 1; test <= C; ++test){

                  cin >> N;

                  adj = vector<vector<int>>(N+1,vector<int>(N+1,0));           

 

                  cin >> T;

                  int a, b;

                  for (int i = 0; i < T; ++i){

                           scanf("%d%d", &a,&b);

                           //방향성이 없기에, [a][b] [b][a] 둘다 ++ 해주는 것에 유의

                           adj[a][b]++;      adj[b][a]++;

                  }

                 

                  vector<int> circuit;

                  getEulerCircuit(1,circuit);

                  printCircuit(circuit);

         }

 

         return 0;

}


EulerCircuit.txt

1

6 8

1 2

1 3

2 4

3 5

4 6

5 6

3 4

4 3


실행결과

1 2 4 3 4 6 5 3 1

계속하려면 아무 키나 누르십시오 . . .


  


-끝-

반응형

'알고리즘 > 알고리즘(C,C++)' 카테고리의 다른 글

[그래프]BFS-기본  (0) 2016.07.09
[그래프]DFS - 끝말 잇기  (0) 2016.04.17
[그래프]DFS - 위상 정렬  (0) 2016.04.15
[그래프]DFS - Deapth First Search 깊이우선 탐색  (0) 2016.04.14
[그래프]기본  (0) 2016.04.14