【图论】Kruskal算法

Kruskal算法

资源here

最小生成树

两大要素

  1. 不能有环
  2. 所有的点都有连接到(一个点也不能落下)

算法步骤

  1. 储存好所有的边,并按权值从小到大进行排序。
  2. 从排序好的边集里依次挑出当前边集中权值最小的边去重新装回到所对应的点和点之间,如果产生了环,就将其略过。
    • 问:如何判断是否有环的产生?
    • 答:并查集
    • 问:依次是不是意味着Kruskal会对每一条作处理?
    • 答:是的,当边数一旦多了起来,Kruskal将会比较耗费时间,所以Kruskal比较适合处理边比较少的图,也就是所谓的稀疏图。而prim普里姆算法(根据点来挑选边)就相对而言比较适合处理边比较多的图,也就是所谓的稠密图。
struct Kruskal{
    点1,点2,路径长
}kru[N*N];
int find(int i){
    是否等于本身?是,返回本身;否
}
bool cmp(Kruskal a,Kruskal b){//路径从小到大排,也可以在结构体内重载运算符
    return a.z<b.z;
}
int main(){
    并查集数组的初始化————刚开始谁都不服谁,谁都只服自己
    读入
    sort(kru+1,kru+1+m,cmp);
    for(int i=1;i<=m;i++){
        找到线段的一个端点的父节点(?)
        找到线段的另一个端点的父节点(?)
        if(如果两者的父节点相等的话,即我们不希望的环产生了)
            continue;
        f[端点1]=端点2;  打不过就加入
    }
}

题目集

Krustal模板题

题目

给定一个 n 个点 mm 条边的无向图,图中可能存在重边和自环,边权可能为负数。

求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

给定一张边带权的无向图 G=(V,E),其中 V表示图中点的集合,E表示图中边的集合,n=|V|,m=|E||。

由 V 中的全部 n 个顶点和 E 中 n−1 条边构成的无向连通子图被称为 G的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G 的最小生成树。

输入格式

第一行包含两个整数 n 和 m。

接下来 mm 行,每行包含三个整数 u,v,w,表示点 u 和点 v 之间存在一条权值为 w 的边。

输出格式

共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

数据范围

1≤n≤10e5,
1≤m≤2∗10e5,
图中涉及边的边权的绝对值均不超过 10001000。

输入样例:

4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4

输出样例:

6

关于题目

  • 关于环的判断
    • 方法一:统计已经添加的边数,在最小生成树的图中,边数=端点数-1;
    • 方法二:笨方法,先记下一个端点的父亲结点,然后在所有其他结点中判断是否存在一个点的父节点与此不同,如果存在,就说明图中存在部分点和其他点是不导通的状态,

代码

#include<bits/stdc++.h>
#define fir(i,a,b) for(int i=a;i<b;i++)
using namespace std;
const int N = 1e5+10,M = 2e5+10;
struct edge{
	int u,v;
	int cost;
	bool operator<(const edge& x)
	{
		return cost<x.cost;
	}
}krus[M];

int f[N];
int find(int x)
{
	return f[x] == x? x:f[x]=find(f[x]);
}
int main()
{
	int n,m;
	cin>>n>>m;
	
	fir(i,1,n+1)//并查集初始化 
	   f[i]=i;
	   
	fir(i,0,m)
		cin>>krus[i].u>>krus[i].v>>krus[i].cost;		
		
	sort(krus,krus+m);
	
	int total = 0; 
	fir(i,0,m)
	{
		int u = krus[i].u,v = krus[i].v;
	    if(find(u)==find(v))//这条边加进来会导致环的出现 
		    continue;	
	
        total += krus[i].cost;
		//f[u] = v; 错误写法,这种写法只是将u和f[u]剥离开来,并将f[u]指向v
		f[find(u)] = find(v);//老大低头认另一帮派的老大才算数			
	} 
	int com = find(1);
	bool flag_connected = 1;
	fir(i,2,n+1)
	{
	
		if(find(i) != com)
		{
			flag_connected = 0;
			break;
		}
	}
	if(flag_connected) cout<<total<<endl;
	else cout<<"impossible"<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/BeautifulWater/p/15077054.html