Algorithm
- Pascal’s triangle 2015.11.01
- 중복조합 2015.10.30
- Kruskal 2015.10.09
Pascal’s triangle
중복조합
중복조합(重複組合, combination with repetition)은 서로 다른 n개의 원소에서 중복을 허락하여 k개를 뽑는 경우의 수이다.
기호로는 nHk로 표기하며, 그 값은 nHk = n+k-1Ck 이다.
예를 들어, 세개의 문자 A,B,C에서 중복을 허용하여 5개를 뽑는 경우의 수는 3H5 = 7C5 = 21이므로 21가지가 된다.
공식을 유도하자면, 중복조합 nHk는 k개의 원소들을 중복을 허용하여 순서에 상관없이 나열하는 것으로 볼 수 있다.
따라서 k개의 빈칸에 중복을 허용하여 n개의 원소(n개 종류의 원소)를 넣는 개수를 구하는 문제로 생각할 수 있다.
이 떄, n가지의 원소를 순서에 상관없이 나열해야 하므로, n - 1개의 칸막이를 두고 나열하는 것으로 생각할 수 있다.
예를 들어, A,B,C 세개의 문자에서 중복을 허용하여 5개를 뽑는 경우의 수를 생각해보자.
1. A A A A A / /
2. A A A A / B
3. A A A / B B /
4. A A / B B B /
5. A / B B B B /
6. B B B B B / /
7. / B B B B / C
8. / B B B / C C
9. / B B / C C C
10. / B / C C C C
11. / / C C C C C
12. A A A A / / C
13. A A A / / C C
14. A A / / C C C
15. A / / C C C C
16. A / B / C C C
17. A / B B / C C
18. A / B B B / C
19. A A / B / C C
20. A A A / B / C
21. A A / B B / C
이런식으로 총 k + n - 1 개의 칸에서 n - 1개의 칸막이를 배치하는 경우의 수로 생각할 수 있다.
따라서 nHk = n+k-1Cn-1 = n+k-1Ck가 된다. 따라서 위의 예는 n = 3이고, k = 5 이므로 3H5 = 3+5-1C5 = 7C5 = 7C2 = 21이 된다.
Kruskal
크루스칼 알고리즘은 최소신장트리(Minimum Spanning Tree)를 구하는 알고리즘이다.
최소신장트리는 위의 그림과 같이 어떤 그래프가 있을 때, 각 노드들을 최소 비용으로, 트리 구조로 연결한 것을 말한다. 트리는 (노드의 수 - 1)개의 간선을 갖고, 싸이클이 생기지 않는 구조를 갖는다.
크루스칼 알고리즘의 순서는 다음과 같다.
1. 간선(Edge)을 비용이 적은 순서대로 정렬한다.
2. 정렬된 간선을 순서대로 추가하는데, 이 때 싸이클이 생기지 않도록 하는 간선만 추가한다.
3. 이를 간선의 수가 노드의 수 - 1 개가 될 때까지 반복한다.
어떤 간선을 추가했을 때 싸이클이 생기는지의 여부는 Disjoint sets(분리집합, 상호배타적집합)을 이용한다. Disjoint sets를 표현하기 위한 자료구조로 disjoint-set data structure를 이용한다. (union–find data structure, merge–find set 라고도 불린다.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | #include <stdio.h> #include <malloc.h> #define N 100 int distance[N][N]; int n; int p[N]; int rank[N]; int sum; typedef struct Edge { int x, y, k; }Edge; Edge edge[N * N]; int cnt; int FindSet(int x) { if (p[x] == x) return x; else p[x] = FindSet(p[x]); } void CreateSet() { for (int i = 0; i < N; i++) { p[i] = i; rank[i] = 0; } } void MergeSet(int x, int y) { x = FindSet(x); y = FindSet(y); if (rank[x] > rank[y]) { p[y] = x; } else { p[x] = y; if (rank[x] == rank[y]) rank[y]++; } } void Merge(Edge *arr, int left, int mid, int right) { int l_idx = left; int r_idx = mid + 1; int idx = left; Edge *temp = (Edge *)malloc(sizeof(Edge) * (right + 1)); while (l_idx <= mid && r_idx <= right) { if (arr[l_idx].k > arr[r_idx].k) temp[idx++] = arr[r_idx++]; else temp[idx++] = arr[l_idx++]; } while (l_idx <= mid) temp[idx++] = arr[l_idx++]; while (r_idx <= right) temp[idx++] = arr[r_idx++]; for (int i = left; i <= right; i++) arr[i] = temp[i]; free(temp); } void MergeSort(Edge *arr, int left, int right) { if (left < right) { int mid = (left + right) / 2; MergeSort(arr, left, mid); MergeSort(arr, mid + 1, right); Merge(arr, left, mid, right); } } int main() { int i, j; //freopen("input.txt", "r", stdin); scanf("%d", &n); for (i = 0; i < n; i++) for (j = 0; j < n; j++) scanf("%d", &distance[i][j]); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { if (j > i) { edge[cnt].x = i; edge[cnt].y = j; edge[cnt++].k = distance[i][j]; } } } MergeSort(edge, 0, cnt); CreateSet(); i = j = 0; while (i < n - 1) { int u = FindSet(edge[j].x); int v = FindSet(edge[j].y); // 싸이클이 안생길 떼 if (u != v) { MergeSet(u, v); sum += edge[j].k; i++; } j++; } printf("%d\n", sum); return 0; } | cs |