input | output |
3 3 1 2 1 2 3 2 1 3 3 |
3 |
해당 문제는 최소 비용 신장 트리(MST)를 사용하는 알고리즘 중에서 Kruskal 알고리즘과 Prim알고리즘으로 해결 가능하다. 필자는 Kruskal 알고리즘을 사용하였다.
Kruskal 알고리즘은 그리디 알고리즘이다. 그리디 알고리즘은 매 선택의 순간 마다 가장 최선의 선택을 하여 최종적인 해답에 도달하는 방법이다. 최선이라고 선택하였던 지역적인 해답들을 모아서 최종적인 해답에 도달했다고 해도, 그 해답이 전역적으로 최선이라는 보장은 없다. 하지만 Kruskal 알고리즘은 최선의 해답을 주는 것으로 증명되었다.
Kruskal 알고리즘은 최소 비용 신장 트리가 최소 비용의 간선으로 구성됨과 동시에 사이클을 포함하지 않는다는 조건에 근거하여, 각 단계에서 사이클을 이루지 않는 최소 비용 간선을 선택한다.
Kruskal 알고리즘의 단계는 다음과 같다.
- 그래프의 간선들을 가중치의 오름차순으로 정렬한다.
- 정렬된 간선들의 리스트에서 사이클을 형성하지 않는 간선을 찾아서 현재 최소 비용 신장 트리의 집합에 추가한다.
- 만약 사이클을 형성하면 간선에서 제외된다.
해당 문제에서 주어진 입력으로 그래프를 그려보면 아래와 같다. (그림이 허접한 점 양해부탁드립니다.)
위 그래프에서 가중치를 기준으로 정렬하면 아래와 같다.
(1,2) | (2,3) | (1,3) |
1 | 2 | 3 |
다음, 간선을 선택하여 집합에 추가한다.
간선 (1,2) 선택 → 간선 (2,3) 선택 → 간선 (1,3) 선택 순서 이다.
간선 (1,3) 선택시에 사이클이 형성되므로 간선 (1,3)은 제외하고 동시에 간선 선택이 종료되며 총 가중치 합은 1+2=3 이다.
전체 소스 코드는 아래와 같다.
#include <stdio.h>
#include <stdlib.h>
#define MAX 100001
#pragma warning(disable:4996)
typedef struct edge { // 그래프 간선 정의
int start, end, weight;
}edge;
int compare(const void* a, const void* b) { // 퀵 정렬 비교 함수 재정의
struct edge* x = (struct edge*)a;
struct edge* y = (struct edge*)b;
return x->weight - y->weight;
}
/* 전역 변수 선언 */
int parent[MAX];
edge E[MAX];
int find(int x) {
if (x == parent[x]) {
return x;
}
else {
return parent[x] = find(parent[x]);
}
}
void union_find(int a, int b) {
a = parent[a];
b = parent[b];
parent[a] = b;
}
int main() {
int v, e;
scanf("%d%d", &v, &e);
for (int i = 0; i < e; i++) { // 간선 삽입
int temp1, temp2, temp3;
scanf("%d%d%d", &temp1, &temp2, &temp3);
E[i].start = temp1;
E[i].end = temp2;
E[i].weight = temp3;
}
for (int i = 1; i <= v; i++) {
parent[i] = i;
}
qsort(E, e, sizeof(struct edge), compare); // 가중치를 기준으로 정렬
int j = 0;
int accepted = 0; // 정점 접근
int a, b;
int result = 0;
while (accepted != v - 1) { // 크루스칼 알고리즘 적용
a = E[j].start;
b = E[j].end;
if (find(a) != find(b)) {
union_find(a, b);
accepted++;
result += E[j].weight;
}
j++;
}
printf("%d", result);
return 0;
}
[1197번]
https://www.acmicpc.net/problem/1197
1197번: 최소 스패닝 트리
첫째 줄에 정점의 개수 V(1 ≤ V ≤ 10,000)와 간선의 개수 E(1 ≤ E ≤ 100,000)가 주어진다. 다음 E개의 줄에는 각 간선에 대한 정보를 나타내는 세 정수 A, B, C가 주어진다. 이는 A번 정점과 B번 정점이
www.acmicpc.net
[github]
https://github.com/MinWook6457
MinWook6457 - Overview
MinWook6457 has 20 repositories available. Follow their code on GitHub.
github.com
'알고리즘' 카테고리의 다른 글
[백준] 1260번 - DFS와 BFS (C언어) [Silver2] (0) | 2023.05.28 |
---|---|
[백준] 1407번 - 2로 몇 번 나누어질까 (C언어) [Gold4] (0) | 2023.05.27 |
[백준] 1874번 - 스택 수열 (C언어) [Silver2] (0) | 2023.05.25 |
[백준] 1913번 - 달팽이 (C언어) [Silver3] (2) | 2023.05.24 |
[백준] 1041번 - 주사위 (C언어) - [Gold5] (0) | 2023.05.24 |