學習最小生成樹前提須知
最小生成樹是指一個\(n\)個節點的圖,讓其變成一個僅有\(n-1\)個邊且改變后該圖是一張連通圖,並且該圖最終成為了一棵最小權重生成樹 (小權值邊盡可能留下,大權值邊盡可能刪除)或 最大權重生成樹 (與前者相反)
算法內容
競賽需要用到的點
1、最小生成樹多用於其他算法的過渡使用,不單獨考(xx不要糾結為什么每次都是這一句
2、可用於一種特殊的模板內(即有些邊自始至終都不會用到的這種)
最小生成樹略講
最小生成樹也是一個很簡單的數據結構,它分了三種比較基本的算法 Kruskal 算法 Prim 算法 Boruvka 算法 ,這三個算法各有各的優缺點,其中 Boruvka 算法 在這三個算法中可以算是最優的算法,甚至 可以因為 Boruvka 算法 的特性 卡掉其他兩個算法,但這不是我們的重點,先講一下 Kruskal 算法 其他兩個算法以后再來填坑(xxx 反正Noip之前應該不會了
Kruskal 算法 是基於 並查集 算法 而進行的,很簡單的思路就是,對一張圖,將所有的邊都拆出來,然后對每條邊的邊權進行排序(從大到小,從小到大看題目需要),然后再將邊連回去,連邊的時候判斷兩個點是否被連通了,如果是連通的,那么就將該邊扔了再看下一條邊,如果沒有被連通,那么就將該條邊連上,然后用並查集合並即可
那么根據上面信息我們就能夠寫出代碼了
Kruskal 部分代碼展現
//#define fre yes
#include <cstdio>
const int N = 100005;
struct Node {
int x, y, z;
} kru[N];
int par[N];
void init(int n) {
for (int i = 1; i <= n; i++) {
par[i] = i;
} //起始化
}
int find(int x) {
if(par[x] == x) return par[x];
else return par[x] = find(par[x]);
} //並查集 看兩點是否在同一個圖內
bool cmp(Node x, Node y) {
return x.z < y.z; //看情況修改 優先級給小邊權還是大邊權
}
int main() {
...
for (int i = 1; i <= n; i++) {
scanf....
kru[i].x = x;
kru[i].y = y;
...
}
std::sort(kru + 1, kru + 1 + n, cmp);
for (int i = 1; i <= m; i++) {
int x = find(kru[i].x);
int y = find(kru[i].y);
if(x == y) continue ;
//並查集合並操作,看是否在同一個圖內 如果在就跳過 不在就合並
par[x] = y;
tot++; //這里是判斷最后是否連通
} if(tot == p - 1) ... //(p是點的個數)
}
若你想將處理之后的圖轉換到鄰接表中怎么辦?
const int N = 200005;
int head[N << 1], to[N << 1], ver[N << 1], edge[N << 1];
int tot;
void addedge(int x, int y, int z) {
ver[tot] = y;
edge[tot] = z;
to[tot] = head[x];
head[x] = tot++;
}
for (int i = 1; i <= m; i++) {
int x = find(kru[i].x);
int y = find(kru[i].y);
if(x == y) continue ;
par[x] = y;
addedge(kru[i].x, kru[i].y, kru[i].z);
addedge(kru[i].y, kru[i].x, kru[i].z);
}
這就完成了
完整代碼 參考LuoGu P3366
//#define fre yes
#include <cstdio>
#include <algorithm>
const int N = 1000005;
struct Node {
int x, y, z;
} edge[N];
int par[N];
void init(int n) {
for (int i = 0; i <= n; i++) {
par[i] = i;
}
}
bool cmp(Node x, Node y) {
return x.z < y.z;
}
int find(int x) {
if(x == par[x]) return par[x];
else return par[x] = find(par[x]);
}
int tot, ans;
int main() {
static int n, m;
scanf("%d %d", &n, &m);
init(n);
for (int i = 1; i <= m; i++) {
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
edge[i].x = x;
edge[i].y = y;
edge[i].z = z;
}
std::sort(edge + 1, edge + 1 + m, cmp);
for (int i = 1; i <= m; i++) {
int x = find(edge[i].x);
int y = find(edge[i].y);
if(x == y) continue ;
tot++;
par[x] = y;
ans += edge[i].z;
} if(tot < n - 1) puts("orz");
else printf("%d\n", ans);
return 0;
}