1. 程式人生 > >最小生成樹(MST)三種實現方法C++版本

最小生成樹(MST)三種實現方法C++版本

程式碼如下:

#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 ;
}