给定一个可以求出最小生成树的图,求最小生成树中边权值最大的边
做法,有 Kruskal 和 Prim 两种算法,Kruskal 适合稀疏图,Prim 适合稠密图。
Kruskal :47 ms , 内存 0.8 MB
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std ;
typedef long long LL ;
int n , m , one , two , i ;
int father[2005] ;
struct Node{
int src , des ;
LL value ;
} Edge[10005] ;
bool cmp( const Node &a , const Node &b ){
return a.value < b.value ;
}
int find( int u ){ // 找祖先
return u == father[u] ? u : father[u] = find( father[u] ) ;
}
int Merge( int a , int b ){ // 如果这两个点还不在一集合里,不会形成环 , return 1 ;
one = find( a ) ;
two = find( b ) ;
if( one != two ){
father[two] = one ;
return 1 ;
}
return 0 ;
}
int main(){
LL ans = 0 ;
scanf( "%d%d" , &n , &m ) ;
for( i = 0 ; i <= n ; ++i ) // 初始化祖先为自己
father[i] = i ;
for( i = 1 ; i <= m ; ++i )
scanf( "%d%d%lld" , &Edge[i].src , &Edge[i].des , &Edge[i].value ) ;
sort( Edge+1 , Edge+m+1 , cmp ) ; // 从小到大排序
for( i = 1 ; i <= m ; ++i )
if( Merge( Edge[i].src , Edge[i].des ) )
ans = max( ans , Edge[i].value ) ;
printf( "%lld\n" , ans ) ;
return 0 ;
}
Prim:125 ms , 内存 16.4 MB
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std ;
typedef pair<int,int> P ;
#define INF 1000000005
int n , m ;
int Gragh[2005][2005] ; // 浪费空间
int ans , value , dis[2005] ;
int book[2005] ;
void Prim(){
int u , i , j ;
ans = 0 ;
for( i = 1 ; i <= n ; ++i )
dis[i] = Gragh[1][i] , book[i] = 0 ;
dis[1] = 0 ;
book[1] = 1 ;
for( i = 2 ; i <= n ; ++i ){
int Min = INF ;
for( j = 1 ; j <= n ; ++j )
if( !book[j] && dis[j] < Min )
Min = dis[j] , u = j ;
ans = max( ans , Min ) ;
book[u] = 1 ;
for( j = 1 ; j <= n ; ++j )
if( !book[j] && dis[j] > Gragh[u][j] )
dis[j] = Gragh[u][j] ;
}
cout << ans << endl ;
}
int main(){
int i , j , src , des ;
scanf( "%d%d" , &n , &m ) ;
for( i = 1 ; i <= n ; ++i )
for( j = 1 ; j <= n ; ++j )
Gragh[i][j] = INF ;
for( i = 1 ; i <= n ; ++i ) Gragh[i][i] = 0 ;
for( i = 1 ; i <= m ; ++i ){
scanf( "%d%d%d" , &src , &des , &value ) ;
if( Gragh[src][des] > value )
Gragh[src][des] = Gragh[des][src] = value ; // 取最短的重边
}
Prim() ;
return 0 ;
}
从上面看, Kruskal 比 Prim 要快,而且,占用内存少(这题因为有重边,所以采用邻接矩阵存储更方便
Prim + 堆优化+邻接表 :63ms , 内存 1 MB
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <queue>
using namespace std ;
#define INF 1000000005
typedef pair<int,int> P ;
int n , m ;
int dis[2005] , book[2005] ;
int head[2005] , k ;
int To[10002<<1] , cost[10002<<1] , Next[10002<<1] ; // 因为是无向图,双向存储,开两倍空间
void Add( int src , int des , int value ){
To[++k] = des , cost[k] = value , Next[k] = head[src] , head[src] = k ;
To[++k] = src , cost[k] = value , Next[k] = head[des] , head[des] = k ;
}
void Prim(){
priority_queue< P , vector<P> , greater<P> > Q ;
int i , u , v , w , ans = 0 ;
for( i = 1 ; i <= n ; ++i )
dis[i] = INF ;
Q.push( P( dis[0] , 1 ) ) ;
while( !Q.empty() ){
P top = Q.top() ;
Q.pop() ;
u = top.second ;
if( book[u] == 0 ) ans = max( ans , top.first ) ;// 即使有重边,因为是优先级队列,权更小的先出来,后面的重边不做处理。
book[u] = 1 ;
if( dis[u] < top.first )
continue ;
for( i = head[u] ; i != -1 ; i = Next[i] ){
v = To[i] , w = cost[i] ;
if( dis[v] > w )
Q.push( P( dis[v] = w , v ) ) ;
}
}
cout << ans << endl ;
}
int main(){
scanf( "%d%d" , &n , &m ) ;
int i , src , des , value ;
for( int i = 0 ; i <= n ; ++i ) head[i] = -1 ;
for( i = 1 ; i <= m ; ++i ){
scanf( "%d%d%d" , &src , &des , &value ) ;
Add( src , des , value ) ;
}
Prim() ;
return 0 ;
}