-
原因
回顧一下舊知識
-
概況
在一給定的無向圖G = (V, E) 中,(u, v) 代表連接頂點 u 與頂點 v 的邊(即),而 w(u, v) 代表此邊的權重,若存在 T 為 E 的子集(即)且為無循環圖,使得的 w(T) 最小,則此 T 為 G 的最小生成樹。
\(\omega(t)=\sum\limits_{(u,v)\in t}{\omega (u,v)}\)
最小生成樹其實是最小權重生成樹的簡稱
-
思想
最小生成樹可以用kruskal(克魯斯卡爾)算法或prim(普里姆)算法求出。
一、kruskal(克魯斯卡爾)算法
1. 基本思想
基本思想是:假設連通網G = (V,E),令最小生成樹的初始狀態為只有 n 個頂點而無邊的非連通圖 T =( V , {} ),
圖中每個頂點自成一個連通分量。在E中選擇代價最小的邊,若該邊的頂點分別在T中不同的連通分量上,則將此邊加入到T中
否則,舍去此邊而選擇下一條代價最小的邊。依此類推,直至T中所有頂點構成一個連通分量為止
2. 時間復雜度
克魯斯卡爾的時間復雜度主要由排序方法決定,而克魯斯卡爾的排序方法只與網中邊的條數有關,而與網中頂點的個數無關,
當使用時間復雜度為O(elog2e)的排序方法時,克魯斯卡爾的時間復雜度即為O(log2e),
因此當網的頂點個數較多、而邊的條數較少時,使用克魯斯卡爾算法構造最小生成樹效果較好
ps:記得配和並查集使用
二、prim(普里姆)算法
1. 算法介紹
1).輸入:一個加權連通圖,其中頂點集合為V,邊集合為E;
2).初始化:Vnew = {x},其中x為集合V中的任一節點(起始點),Enew = {},為空;
3).重復下列操作,直到Vnew = V:
a.在集合E中選取權值最小的邊<u, v>,其中u為集合Vnew中的元素,而v不在Vnew集合當中,
並且v∈V(如果存在有多條滿足前述條件即具有相同權值的邊,則可任意選取其中之一);
b.將v加入集合Vnew中,將<u, v>邊加入集合Enew中;
4).輸出:使用集合Vnew和Enew來描述所得到的最小生成樹。
2. 時間復雜度
| 最小邊、權的數據結構 | 時間復雜度(總計) |
|---|---|
| 鄰接矩陣、搜索 | O(V^2) |
| 二叉堆、鄰接表 | O((V + E) log(V)) = O(E log(V)) |
| 斐波那契堆、鄰接表 | O(E + V log(V)) |
通過鄰接矩陣圖表示的簡易實現中,找到所有最小權邊共需O(V)的運行時間。
使用簡單的二叉堆與鄰接表來表示的話,普里姆算法的運行時間則可縮減為O(ElogV),其中E為連通圖的邊數,V為頂點數。
如果使用較為復雜的斐波那契堆,則可將運行時間進一步縮短為O(E+VlogV),
這在連通圖足夠密集時(當E滿足Ω(VlogV)條件時),可較顯著地提高運行速度。
(@百度百科)
-
題目
P3366 【模板】最小生成樹
模板,致敬,我用的kruskal
#include <bits/stdc++.h>
using namespace std;
struct node{
int x,y,len;
}a[200005];
int f[100005];
int cmp(node num1,node num2){
return num1.len<num2.len;
}
int find(int x){
if (x!=f[x]) f[x]=find(f[x]);
return f[x];
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++){
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].len);
}
long long ans=0;
sort(a+1,a+1+m,cmp);
for (int i=1;i<=n;i++) f[i]=i;
for (int i=1;i<=m;i++){
int x=find(a[i].x),y=find(a[i].y);
if (x!=y){
ans+=a[i].len;
f[x]=y;
}
}
printf("%d",ans);
}
P1991 無線通訊網
** 其中主要就是kruskal,注意排序順序和找第幾大的邊 **
#include <bits/stdc++.h>
using namespace std;
int n,m;
int x[1005],y[1005];
int f[1005];
struct node{
int l,r;
double len;
}a[250005];
int h=0;
double ans;
bool cmp(node num1,node num2){
return num1.len<num2.len;
}
int find (int x){
if (x!=f[x]) f[x]=find(f[x]);
return f[x];
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++){
scanf("%d%d",&x[i],&y[i]);
}
for (int i=1;i<=m;i++){
for (int j=i+1;j<=m;j++){
h++;
a[h].l=i;
a[h].r=j;
a[h].len=sqrt(pow(x[i]-x[j],2)+pow(y[i]-y[j],2));
}
}
sort(a+1,a+1+h,cmp);
for (int i=1;i<=m;i++){
f[i]=i;
}
int tot=0;
for (int i=1;i<=h;i++){
int x=find(a[i].l),y=find(a[i].r);
if (x!=y){
++tot;
ans=a[i].len;
f[x]=y;
}
if (tot==m-n){
printf("%.2lf",ans);
break;
}
}
return 0;
}
