最小生成樹
阿新 • • 發佈:2020-07-13
最小生成樹的定義:一個有 n 個結點的連通圖的生成樹是原圖的極小連通子圖,且包含原圖中的所有 n 個結點,並且有保持圖連通的最少的邊。最小生成樹可以用kruskal(克魯斯卡爾)演算法或prim(普里姆)演算法求出。
定理:任意一顆最小生成樹一定包含無向圖中權值最小的邊。
在一給定的無向圖G = (V, E) 中,(u, v) 代表連線頂點 u 與頂點 v 的邊(即),而 w(u, v) 代表此邊的權重,若存在 T 為 E 的子集(即)且為無迴圈圖,使得的 w(T) 最小,則此 T 為 G 的最小生成樹。 最小生成樹其實是最小權重生成樹的簡稱。 下面介紹兩種演算法:Kruskal和Prim。const int maxn=1e5; struct rec { int x,y,z; }edge[maxn];int fa[maxn],n,m,ans; bool operator <(rec a,rec b) { return a.z<b.z; } int get(int x) { if(x==fa[x]) return x; return fa[x]=get(fa[x]); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z); sort(edge+1,edge+n+1);//按邊權排序 for(int i=1;i<=n;i++) fa[i]=i;//並查集初始化; for(int i=1;i<=n;i++) { int x=get(edge[i].x); int y=get(edge[i].y); if(x==y) continue; fa[x]=y; ans+=edge[i].x; }//求最小生成樹 printf("%d\n",ans); }
Prim:
最初,僅先確定1號節點屬於最小生成樹。任意時刻假設已經確定屬於最小生成樹的節點集合為T,剩餘節點集合為S。Prim演算法找到,即兩個端點分別屬於集合S,T的權值最小的邊,然後把點x從集合S中刪除,加入到集合T,並把z累加到答案中。
具體一點,維護陣列d:若x∈S,則d[x]表示節點x與集合T的節點之間權值最小的邊的權值。若x∈T,則d[x]就等於x被加入T時選出的最小值的權值。
const int maxn=1e5; int a[manx][maxn],d[maxn],n,m,ans; bool v[maxn]; void prim() { memset(d,0x3f,sizeof(d)); memset(v,0,sizeof(v)); d[1]=0; for(int i=1;i<n;i++) { int x=0; for(int j=1;j<=n;j++) if(!v[j]&&(x==0||d[j]<d[x])) x=j; v[x]=1; for(int y=1;y<=n;y++) if(!v[y]) d[y]=min(d[y],a[x][y]); } } int main() { scanf("%d%d",&n,&m); memset(a,0x3f,sizeof((a))); for(int i=1;i<=n;i++) a[i][i]=0; for(int i=1;i<=n;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); a[y][x]=a[x][y]=min(a[x][y],z); }//構建鄰接矩陣 prim(); for(int i=2;i,=n;i++) ans+=d[i];//求最小生成樹 printf("%d\n",ans); }
兩種方法不做比較,因題和個人而定。