최소신장트리(MST)

2022. 8. 28. 23:29C++/알고리즘

신장트리 = Spanning Tree

- 그래프가 존재할때 모든 정점에 대하여 최소한의 연결만 남긴 그래프.

- 한 그래프에서 여러가지의 신장트리가 존재할 수 있다.

- 모든 정점을 포함해야 하며 모두 연결 되어있어야 함.(버텍스가 N개일때 간선은 N-1개)

- 사이클이 존재하면 안된다.

- DFS나 BFS로 구할 수 있다.

 

최소신장트리 = Minimum Spanning Tree

- 간선에 가중치가 있는 그래프의 신장 트리에서 간선의 가중치의 합이 최소가 되는 신장 트리.

- 주요 방법으론 Prim 알고리즘와 Kruskal 알고리즘이 있음.

 

Kruskal 알고리즘

- 간선을 가중치 기준 오름차순으로 정렬하여 순서대로 판단해나가는 알고리즘.

- 간선을 고를 때 사이클이 나오지 않는지 판단해야 함.

- 사이클 판단으론 Union-Find 알고리즘이 사용됨.

 

Union-Find 알고리즘

- 각 버텍스에 대해 부모 노드를 Find 하고 Union 하는 알고리즘.

- 두 버텍스에 대하여 각각 최종 부모노드를 Find 했을때 두 버텍스의 최종 부모노드가 같다면 해당 간선은 사이클을 발생시킨다고 판단 가능함.

- Kruskal 알고리즘에서 사용 시 두번째 버텍스의 최종 부모가 첫번째 버텍스의 최종 부모가 되도록 업데이트를 해주어야 Find가 계속 이루어질 수 있음.

 

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

class edge
{
public:
	int first_vertex;
	int second_vertex;
	int value;

	edge(int n1, int n2, int n3)
	{
		first_vertex = n1;
		second_vertex = n2;
		value = n3;
	}
};

bool edge_cmp(edge& e1, edge& e2)
{
	if (e1.value < e2.value) return true;
	else return false;
}

bool Union_Find(int parent[], int first_vertex, int second_vertex)
{
	int fv = first_vertex;
	int sv = second_vertex;
	while (!(fv == parent[fv]))
	{
		fv = parent[fv];
	}
	while (!(sv == parent[sv]))
	{
		sv = parent[sv];
	}
	if (fv == sv)
	{
		return false;
	}
	else
	{
		// 두번째 버텍스의 최종 부모가 첫번째 버텍스의 최종 부모가 됨
		parent[sv] = parent[fv];
		return true;
	}
}

int Kruskal(vector<edge> edge_vector, int vertex_number)
{
	//MST의 가중치의 합을 리턴
	int parent[10001];
	for (int i = 0; i < 10001; i++)
	{
		parent[i] = i;
	}

	int rtn = 0;
	int index = 0;
	int edge_count = 0;
	int cmd = edge_vector.size();
	while (cmd--)
	{
		edge current_edge = edge_vector[index++];
		if (Union_Find(parent, current_edge.first_vertex, current_edge.second_vertex))
		{
			edge_count++;
			rtn += current_edge.value;
		}
	}

	return rtn;
}

int main()
{
	int vertex_number;
	int edge_number;
	vector<edge> edge_vector;

	cin >> vertex_number >> edge_number;

	int cmd = edge_number;

	while (cmd--)
	{
		int n1;
		int n2;
		int n3;

		cin >> n1 >> n2 >> n3;

		if (n1 > n2)
		{
			int tmp = n1;
			n1 = n2;
			n2 = tmp;
		}

		edge_vector.push_back(edge(n1, n2, n3));
	}

	sort(edge_vector.begin(), edge_vector.end(), edge_cmp);

	cout << Kruskal(edge_vector, vertex_number) << endl;
}

백준 1197번 문제(https://www.acmicpc.net/problem/1197)

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

컵라면(그리디 알고리즘)  (1) 2022.10.04
라면 사기(그리디 알고리즘)  (0) 2022.10.04
백조의 호수(BFS 최적화)  (0) 2022.08.31
우선순위 큐(Priority Queue)  (0) 2022.08.30
다이나믹 프로그래밍  (0) 2022.08.29