사이클 게임은 두 명의 플레이어가 차례대로 돌아가며 진행하는 게임으로, 선 플레이어가 홀수 번째 차례를, 후 플레이어가 짝수 번째 차례를 진행한다. 게임 시작 시 0 부터 n − 1 까지 고유한 번호가 부여된 평면 상의 점 n 개가 주어지며, 이 중 어느 세 점도 일직선 위에 놓이지 않는다. 매 차례 마다 플레이어는 두 점을 선택해서 이를 연결하는 선분을 긋는데, 이전에 그린 선분을 다시 그을 수는 없지만 이미 그린 다른 선분과 교차하는 것은 가능하다. 게임을 진행하다가 처음으로 사이클을 완성하는 순간 게임이 종료된다. 사이클 C는 플레이어가 그린 선분들의 부분집합으로, 다음 조건을 만족한다.
C에 속한 임의의 선분의 한 끝점에서 출발하여 모든 선분을 한 번씩만 지나서 출발점으로 되돌아올 수 있다.
문제는 선분을 여러 개 그리다 보면 사이클이 완성 되었는지의 여부를 판단하기 어려워 이미 사이클이 완성되었음에도 불구하고 게임을 계속 진행하게 될 수 있다는 것이다. 이 문제를 해결하기 위해서 게임의 진행 상황이 주어지면 몇 번째 차례에서 사이클이 완성되었는지, 혹은 아직 게임이 진행 중인지를 판단하는 프로그램을 작성하려 한다.
입력으로 점의 개수 n과 m 번째 차례까지의 게임 진행 상황이 주어지면 사이클이 완성 되었는지를 판단하고, 완성되었다면 몇 번째 차례에서 처음으로 사이클이 완성된 것인지를 출력하는 프로그램을 작성하시오.
두 개의 점을 선분으로 이을경우 두 개의 점은 같은 집합에 속한다.
이때 서로 다른 집합의 점을 잇는 경우라면 사이클이 완성되지 않지만,
이미 같은 집합의 점을 잇는 경우라면 사이클이 완성된다.
두 점을 입력 받았을 때 두 점이 같은 집합인지 먼저 검사하고 같은 집합이라면 사이클에 해당되므로 해당 차례의 번호를 출력한다.
집합과 관련된 연산은 union-find 알고리즘을 활용하였다.
/*
입력을 끝까지 받지 않고 사이클 완성되었을 때 break; 하면 훨씬 빨리 프로그램이 끝나긴함
당연하지만 isInit같은 변수 안쓰고 그냥 전부다 초기화하면 변수 하나가 줄어서 그런지 메모리도 더 적었음
*/
#include <iostream>
using namespace std;
struct Node {
int rank;
int root;
};
Node* nodes;
int findSet(int x){
if (x == nodes[x].root){
return x;
}
else {
return nodes[x].root = findSet(nodes[x].root);
}
}
void unionSet(int x, int y){
x = findSet(x);
y = findSet(y);
if (x == y) return;
if (nodes[x].rank > nodes[y].rank){
nodes[y].root = x;
}
else if (nodes[x].rank < nodes[y].rank){
nodes[x].root = y;
}
else {
nodes[x].root = y;
nodes[y].rank++;
}
}
int main(){
cin.tie(0);
ios_base::sync_with_stdio(false);
int n, m;
int answer = 0;
cin >> n >> m;
nodes = (Node*)malloc(sizeof(Node) * n);
for (int i =0; i<n; i++){
nodes[i].rank = 0;
nodes[i].root = i;
}
for (int i=0; i < m; i++){
int v1, v2;
cin >> v1 >> v2;
// isCycle
if (findSet(v1) == findSet(v2)){
answer = i + 1;
}
unionSet(v1, v2);
}
cout << answer << endl;
return 0;
}