最小生成树
设G(V,E)是无向;联通带权图,对图中每一条边(u,v)的权威c[u][v] , 表示联通u与v的代价,如果G的子图T是一颗包含G的所有顶点的树,则称T为G的生成树,生成树上的权总和成为该生成树的耗费,在G的所有生成树中耗费最小的生成树成为G的最小生成树。
一般来说krukskal算法比较简单且适合稠密图,而prim适合稀疏图
- krukskal算法
算法实现:首先将G的n个顶点看成是n个孤立的连通分量,将所有边的权值从小到大排序,然后从第一条边开始,依边递增查看,若看到第i条边(u,v)时,如果端点属于不同的连通分量中的顶点时(用到并查集),就用当前的边(u,v)将其连接起来,当只剩下一个联通分量的时候,该连通分量就为G的一颗最小生成树。
模板:
#include <cstdio>
#include <algorithm>
using namespace std ;
const int N = 2e5 + 5 ;
int s[N] ;
struct node {
int x , y , w ;
};
node egde[N] ;
bool cmp(node a , node b){
return a.w < b.w ;
}
int find(int x){
if (x != s[x])
s[x] = find(s[x]) ;
return s[x] ;
}
int main(){
int n , m ;
scanf ("%d%d",&n,&m) ;
for (int i = 1 ; i <= n ; ++ i)
s[i] = i ;
for (int i = 1 ; i <= m ; ++ i)
scanf ("%d%d%d",&egde[i].x,&egde[i].y,&egde[i].w) ;
sort(egde+1,egde+1+m,cmp) ;
int cost = 0 ;
for (int i = 1 ; i <= m ; ++ i){
int a = find(egde[i].x) , b = find(egde[i].y) ;
if (a != b){
s[a] = b ;
n -- ;
cost += egde[i].w ;
}
if (n == 1){
printf ("%d\n",cost) ;
return 0 ;
}
}
if (n > 1)
printf ("orz\n") ;
return 0 ;
}
prim算法
详解移步大佬:prim解说
主要就是从未选择的点中选择权值最小的边所对的点加入生成树,直到全部点都加入了生存树。
#include <cstdio>
#include <vector>
using namespace std ;
const int N = 5005 ;
const int INF = 1e6 ;
int low[N] , graph[N][N] ;
bool done[N] ;
int n , m ;
void prim(int u){ //起点u
int sum = 0 ;
for (int i = 1 ; i <= n ; ++ i){
low[i] = graph[u][i] ; //寻找与起点u 相连的边的权值
done[i] = false ;
}
done[u] = true ;
int j = 1 ;
for (int i = 1 ; i < n ; ++ i){ //处理其他n-1个顶点
int min = INF ;
//在low数组中找未加入生成树的最小值
for (int k = 1 ; k <= n ; ++ k) {
if ((low[k] < min) && (!done[k])){
min = low[k] ;
j = k ;
}
}
//加入结点j
done[j] = true ;
sum += low[j] ;
for (int k = 1 ; k <= n; ++ k){
if (low[k] > graph[j][k] && (!done[k])){
low[k] = graph[j][k] ; //更新与j相连的权值
}
}
}
printf ("%d\n",sum) ;
}
int main(){
scanf ("%d%d",&n,&m) ;
for (int i = 1 ; i <= n ; ++ i){ //初始化
for (int j = 1 ; j <= m ;++ j){
if (i == j) graph[i][j] = 0 ;
else graph[i][j] = INF ;
}
}
while(m--){ //建图
int u , v , w ;
scanf ("%d%d%d",&u,&v,&w) ;
//路径重复时选择权值小的保存
graph[u][v] = graph[u][v] < w ? graph[u][v] : w ;
graph[v][u] = graph[u][v] ;
}
prim(1) ;
return 0 ;
}
不过以上的prim做法时间复杂度较大,可以用堆优化降低时间复杂度
#include <cstdio>
#include <vector>
#include <queue>
using namespace std ;
const int N = 5005 ;
const int INF = 1e6 ;
struct edge{
int to , w ;
edge(int a , int b){to = a ; w = b ;} ;
};
struct node{
int id , dis ;
node(int a , int b){id = a ; dis = b ;} ;
bool operator < (const node &c)
const {
return dis > c.dis ;
}
};
int d[N] ;
bool done[N] ;
int n , m , cnt ;
vector<edge> e[N] ;
void prim(int u){ //起点
int sum = 0 ;
for (int i = 1 ; i <= n ; ++ i){
d[i] = INF ;
done[i] = false ;
}
priority_queue<node> q ;
d[u] = 0 ;
q.push(node(u,0)) ;
while(!q.empty() && cnt<n){
int head = q.top().id ;
q.pop() ;
if (done[head]) continue ;
done[head] = true ;
++ cnt ;
sum += d[head] ;
for (int i = 0 ; i < e[head].size() ; ++ i){
edge t = e[head][i] ;
if (done[t.to]) continue ;
if (d[t.to] > t.w){
d[t.to] = t.w ;
q.push(node(t.to,d[t.to])) ;
}
}
}
if (cnt == n) printf ("%d\n",sum) ;
else printf ("orz\n") ;
}
int main(){
scanf ("%d%d",&n,&m) ;
while(m --){
int u , v , w ;
scanf ("%d%d%d",&u,&v,&w) ;
e[u].push_back(edge(v,w)) ;
e[v].push_back(edge(u,w)) ;
}
prim(1) ;
}