最小生成樹(MST)詳解+題目


  • 原因

    回顧一下舊知識

  • 概況

    在一給定的無向圖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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM