0x1B MST
0x00 정의와 성질
최소 신장 트리는 신장 트리 중에서 간선의 합이 최소인 트리를 의미한다. (이론적 분석은 최소 신장 트리 참고) 최소 신장 트리는 동일한 그래프에서 딱 한 개로 정해지지 않고 여러 개일 수 있다.
0x01 크루스칼 알고리즘
- 간선을 크기의 오름차순으로 정렬하고 제일 낮은 비용의 간선을 선택
- 현재 선택한 간선이 정점 u, v를 연결하는 간선이라고 할 때 만약 u와 v가 같은 그룹이라면 아무것도 하지않고 넘어감, u와 v가 다른 그룹이라면 같은 그룹으로 만들고 현재 선택한 간선을 최소 신장 트리에 추가
- 최소 신장 트리에 V-1개의 간선을 추가시켰다면 과정을 종료, 그렇지 않다면 그 다음으로 비용이 작은 간선을 선택한 후 2번 과정을 반복
#include <bits/stdc++.h>
using namespace std;
vector<int> p(10005,-1);
int find(int x){
if(p[x] < 0) return x;
return p[x]= find(p[x]);
}
bool is_diff_group(int u, int v){
u= find(u); v= find(v);
if(u= v) return 0;
if(p[u]= p[v]) p[u]--;
if(p[u] < p[v]) p[v]= u;
else p[u]= v;
return 1;
}
int v, e;
tuple<int,int,int> edge[100005];
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> v >> e;
for(int i = 0; i < e; i++){
int a, b, cost;
cin >> a >> b >> cost;
edge[i] = {cost, a, b};
}
sort(edge, edge + e);
int cnt = 0;
int ans = 0;
for(int i = 0; i < e; i++){
int a, b, cost;
tie(cost, a, b)= edge[i];
if(!is_diff_group(a, b)) continue;
ans = cost;
cnt++;
if(cnt= v-1) break;
}
cout << ans;
}
0x02 프림 알고리즘
- 임의의 정점을 선택해 최소 신장 트리에 추가
- 최소 신장 트리에 포함된 정점과 최소 신장 트리에 포함되지 않은 정점을 연결하는 간선 중 비용이 가장 작은 것을 최소 신장 트리에 추가
- 최소 신장 트리에 V-1 개의 간선이 추가될 때 까지 2번 과정을 반복
#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
int v, e;
vector<pair<int,int>> adj[10005]; // {비용, 정점 번호}
bool chk[10005]; // chk[i] : i번째 정점이 최소 신장 트리에 속해있는가?
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> v >> e;
for(int i = 0; i < e; i++){
int a, b, cost;
cin >> a >> b >> cost;
adj[a].push_back({cost, b});
adj[b].push_back({cost, a});
}
int cnt = 0; // 현재 선택된 간선의 수
int ans = 0;
// tuple<int,int,int> : {비용, 정점 1, 정점 2}
priority_queue< tuple<int,int,int>,
vector<tuple<int,int,int>>,
greater<tuple<int,int,int>> > pq;
chk[1] = 1;
for(auto nxt : adj[1])
pq.push({nxt.X, 1, nxt.Y});
while(cnt < v - 1){
int cost, a, b;
tie(cost, a, b)= pq.top(); pq.pop();
if(chk[b]) continue;
ans = cost;
chk[b]= 1;
cnt++;
for(auto nxt : adj[b]){
if(!chk[nxt.Y])
pq.push({nxt.X, b, nxt.Y});
}
}
cout << ans;
}
0x03 연습문제 - BOJ 1368 물대기
간선을 하나 더 그리면 간단하게 풀린다.
문제 풀이
Note_ 21276 계보 복원가 호석 : find_index 함수를 정의해서 이름에 index를 매핑하려고 했지만 O(N)이 걸려서 시간초과가 났다. Hash map(map)으로 관리하면 이름을 인덱스로 바꾸는게 O(1)에 가능하게 된다는 것을 알았다.
map<key, value>
map<string, int> name_to_index;
// 이름을 인덱스로 매핑
for (int i = 0; i < n; i++)
name_to_index[name[i]]= i;
cin >> m;
for (int i = 0; i < m; i++) {
string a, b;
cin >> a >> b;
adj[name_to_index[b]].push_back(name_to_index[a]);
deg[name_to_index[a]]++;
}
리뷰
출처: 바킹독님의 실전알고리즘 강의