7-3 最小生成树-kruskal (10 分)(思路+详解+并查集详解+段错误超时解决)宝 Come
一:前言
本題需要用到并查集的知識,建議先學完并查集后再看看本題
二:題目
題目給出一個無向連通圖,要求求出其最小生成樹的權值。
溫馨提示:本題請使用kruskal最小生成樹算法。
輸入格式:
第一行包含兩個整數 N(1<=N<=1x10 6
),M(1<=M<=1x10 6
) 表示該圖共有 N 個結點和 M 條無向邊。接下來 M 行每行包含三個整數 X ,表示有一條長度為 Z 的無向邊連接結點 X
輸出格式:
輸出一個整數表示最小生成樹的各邊的長度之和。
輸入樣例:
4 5 1 2 2 1 3 2 1 4 3 2 3 4 3 4 3輸出樣例:
7三:介紹kruskal
1:介紹kruskal
2:演示算法過程就是并查集的過程
形成環:即頂點3和5的根節點為同一個結點那么就不用合并
四:思路
思路:
1.用kruskal算法是從邊的角度出發的每次選取最小的邊
2.這里涉及到并查集,回歸到本題就是先將每個頂點當成一個聯通分量(也是一棵樹)
并設置其父節點都為 -1,表示根節點,如果兩個連通分量根節點不同,那么將其合并,
同時更新其中一個連通分量的根節點為另一個連通分量的根節點
那么到最后就會形成一個根節點,此時圖的各個結點也是連通的,我們在每次計算是否合并
兩個連通分量的時候,我們就已將權值進行相加,那么這樣的結果是最小生成樹
3.那么我們就需要對邊進行升序排序了,按順序每次選取最小的邊
4.這里考慮到還要對權值進行升序處理,而且是按一組數據中的某個元素 升序處理
那么這里我們就不再用鄰接矩陣和鄰接表來存數據了,用結構體數組來存每組數據
同時通過重寫sort方法來處理升序問題
5.那么通過上方的分析你還有另外一個收獲,判斷圖是否連通,哈哈哈,如果最后的father數組
中每個結點的根節點都是同一個值 那么說明他是連通的 (這里的結點指的是father數組當中的下標)
五:上碼
/**思路:1.用kruskal算法是從邊的角度出發的每次選取最小的邊2.這里涉及到并查集,回歸到本題就是先將每個頂點當成一個聯通分量(也是一棵樹) 并設置其父節點都為 -1,表示根節點,如果兩個連通分量根節點不同,那么將其合并,同時更新其中一個連通分量的根節點為另一個連通分量的根節點那么到最后就會形成一個根節點,此時圖的各個結點也是連通的,我們在每次計算是否合并兩個連通分量的時候,我們就已將權值進行相加,那么這樣的結果是最小生成樹 3.那么我們就需要對邊進行升序排序了,按順序每次選取最小的邊4.這里考慮到還要對權值進行升序處理,而且是按一組數據中的某個元素 升序處理那么這里我們就不再用鄰接矩陣和鄰接表來存數據了,用結構體數組來存每組數據同時通過重寫sort方法來處理升序問題 5.那么通過上方的分析你還有另外一個收獲,判斷圖是否連通,哈哈哈,如果最后的father數組只輸出一個值 那么說明他是連通的 */ #include<bits/stdc++.h> using namespace std; #define maxx 1000010//這里需要7位數//定一結構體數組來存每組數據 struct Node{int from;int to;int val; }node[maxx]; int father[maxx];bool sort_val(Node a,Node b){return a.val < b.val; }//查詢元素的根節點 int find( int a ){int r=a;while(father[r]!=r)r=father[r]; //找到他的前導結點int i=a,j;while(i!=r){ //路徑壓縮算法j=father[i]; //記錄x的前導結點father[i]=r; //將i的前導結點設置為r根節點i=j;}return r; }//合并根節點不同的聯通分量 void merg(int a,int b){// int a = find(x);//查詢x的根節點 // int b = find(y);//查詢y的根節點 // if(a != b){father[a] = b; // } }int main(){int n,m;int sum = 0;//cin >> n >> m;scanf("%d%d",&n,&m);//初始化father數組 將其每個頂點的根節點設置為自己的節點號for(int i = 1; i <= n; i++){father[i] = i;} for(int i = 0; i < m; i++){ //cin >> node[i].from >> node[i].to >> node[i].val;scanf("%d%d%d",&node[i].from,&node[i].to,&node[i].val);}sort(node,node+m,sort_val);// for(int i = 0; i < m; i++){ // cout << node[i].val << endl; // }int count = 0;for(int i = 0; i < m; i++){ if(count == n - 1){//n個頂點需要 n - 1邊break;}int a = find(node[i].from);int b = find(node[i].to);if(a != b){father[a] = b;sum += node[i].val; count++;} }printf("%d",sum);// cout << sum; }六:知識速遞(對并查集不了解的兄弟們可以了解下)
這道題用到了并查集,所以我就學了一下并查集,所以把自己的見解也分享給大家(建議 先看視頻 再瀏覽 博客 再自己敲一遍 學習效率高而已,我總是亂著來 以為看幾篇博客就會了,其實最后還是老老實實 去B站看大佬講解視頻 才搞懂)
1:并查集
并查集是一種樹型的數據結構,
用于處理一些不相交集合(Disjoint Sets)的合并及查詢問題
1:查詢元素a和元素b是否屬于同一組
2:合并元素a和元素b所在組 (將有相同元素的元素 合并為一個組 )
3:需要初始化一個數組存放父節點,其索引值 代表元素
2:并查集的AC代碼(模板`)
/*
并查集是一種樹型的數據結構,
用于處理一些不相交集合(Disjoint Sets)的合并及查詢問題
1:查詢元素a和元素b是否屬于同一組
2:合并元素a和元素b所在組 (將有相同元素的元素 合并為一個組 )
3:需要初始化一個數組存放父節點,其索引值 代表元素
*/
上方的find函數 效率不高,當處理大數據時,使用并查集查找時,如果查找次數很多,那么使用樸素版的查找方式肯定要超時。比如,有一百萬個元素,每次都從第一百萬個開始找,這樣一次運算就是106,如果程序要求查找個一千萬次,這樣下來就是1013,肯定要出問題的。
所以有了壓縮路徑的算法(就是一棵樹只有葉節點)
int find( int a ){int r=a;while(Father[r]!=r)r=Father[r]; //找到他的前導結點int i=a,j;while(i!=r){ //路徑壓縮算法j=Father[i]; //記錄x的前導結點Father[i]=r; //將i的前導結點設置為r根節點i=j;}return r; }七:超時和段錯誤解決
1.超時建議將cin cout改為scanf 和printf
2.段錯誤建議將上方的開辟最大值調至 七位數
總結
以上是生活随笔為你收集整理的7-3 最小生成树-kruskal (10 分)(思路+详解+并查集详解+段错误超时解决)宝 Come的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中药最快丰胸配方一个月吃什么最有效
- 下一篇: 敲胆经的最佳时间是什么时候