最小生成樹(MST)三種實現方法C++版本
阿新 • • 發佈:2019-01-31
程式碼如下:
#include<fstream> #define MAXN 100 using namespace std ; ifstream cin("test.txt") ; ofstream cout("MST.out") ; const int infinity = 1000000 ; int cost[MAXN][MAXN],g[MAXN][MAXN],n,m=0; int dis[MAXN],f[MAXN],w[MAXN],u[MAXN],v[MAXN],vis[MAXN]; //u,v,w陣列分別表示邊的起始點,終止點,和權值 //f用做並查集,vis用來記錄dfs時某點是否被訪問過 int prim(int v0) { int i,j,distance,k; int ans = 0; for(i = 0;i < n;i++) dis[i] = cost[v0][i]; //dis[i]表示所有與i相連的邊中,權值最小的那個 for(i = 0;i < n-1; i++) { distance = infinity; for(j = 0;j < n; j++) if(dis[j] && distance > dis[j]) { //在所有U集合和V-U集合的連邊中選出最小一條 distance = dis[j]; k = j; } ans += dis[k]; dis[k] = 0; //k節點加入集合U for(j = 0;j < n;j++) if(cost[j][k] < dis[j]) dis[j] = cost[j][k]; } return ans; } void sort() //普通的排序O(m^2),用快排可使時間複雜度均攤為O(mlogm). { for (int i = 1 ; i<=m ; i++) for (int j = i+1 ; j<=m ; j++) if (w[i]>w[j]) { int tmp = w[i] ; w[i] = w[j]; w[j] = tmp ; tmp = u[i] ; u[i] = u[j]; u[j] = tmp ; tmp = v[i] ; v[i] = v[j]; v[j] = tmp ; } } int find(int i) //並查集,查詢父親 { if ( f[i]!=i ) f[i]=find( f[i] ) ; //路徑壓縮 return f[i] ; } void Union(int i,int j) //兩個並查集的合併 { i=find( i ) ; j=find( j ) ; if (i != j ) f[j] = i ; } int Kruskal() { int total=0 , answer = 0 ; //total表示邊數,當邊數為n-1時,演算法結束 sort(); for (int i=1 ; i<=m ; i++) cout << w[i] << endl ; for (int i=0 ; i<n ; i++) f[i] = i; for (int i = 1 ; i<= m ; i++) if ( find(u[i]) != find(v[i]) ) //用並查集判斷兩個節點是否在同一個集合中 { Union(u[i],v[i]) ; answer = answer + w[i] ; ++ total ; if (total == n-1 ) break ; } return answer ; } void dfs(int u) //圖的深度優先遍歷 { vis[u] = 1 ; for (int i = 0 ; i<n ; i++) if ( ( g[u][i]!=infinity ) && !vis[i] ) dfs(i) ; } bool QuBian(int th) //判斷去掉第th條邊後圖還能不能連通 { g[ u[th] ][ v[th] ] = infinity ; g[ v[th] ][ u[th] ] = infinity ; for(int i=0; i<n ;i++) vis[i]=0; dfs(u[th]); int total = 0 ; for (int i = 0 ; i<n ; i++) if (vis[i]) ++total ; if ( total==n ) return true ; else return false ; } int work() { int answer = 0 ; //answer表示最終結果 int t1=0,t2=0 ; //t1表示去掉的邊數,t2表示保留的邊數 for (int i=0 ; i<n ; i++) for (int j=0 ; j<n ; j++) g[i][j] = cost[i][j] ; int k; for ( k=m ; k>0 ; k--) //k表示當前圖中剩下的邊數 if ( !QuBian(k) ) { g[ u[k] ][ v[k] ] = w[k] ; g[ v[k] ][ u[k] ] = w[k] ; ++t1 ; answer += w[k] ; if (t1+k == n-1 ) break ; } //當圖中剩下的邊數和保留的邊數之和為n-1時,不再去邊 else ++t2; for (int j=k ; j>0 ; j--) answer+=w[j] ; return answer ; } int main(){ cin >> n >> m; for (int i=0 ; i<n ; i++) for (int j=0 ; j<n ; j++) cost[i][j] = infinity ; for (int i=1 ; i<=m ; i++) { cin >> u[i] >> v[i] >> w[i] ; cost[u[i]][v[i]] = w[i] ; cost[v[i]][u[i]] = w[i] ; } cout << prim(0) << endl; cout << Kruskal() << endl; cout << work() << endl; return 0 ; }