最小生成树及模板题

一个连通图的生成树是一个极小连通子图,其中含有图中的全部顶点和构成一棵树的(n-1)条边。
最小生成树:图中所有生成树中具有边上的权值之和最小的树。
最小生成树是最基本的图论问题之一,可由Kruskal(克鲁斯卡尔)算法和Prim(普里姆)算法求解。
两种算法构造最小生成树的原理不同。
Prim算法核心:从某一顶点出发,每次贪心选择与该点连通且未曾选入的边权最小的顶点加入集合,考虑每次新选入的顶点可能造成的影响,需修改候选边的边权和前驱结点,重复操作,直到选完所有顶点(除初始顶点外,只用选n-1个)。
模板题:http://hihocoder.com/problemset/problem/1097
代码如下:

#include
using namespace std;
const int maxn=1005,INF=0x3f3f3f3f;
int n,edge[maxn][maxn];
void init(){
     
	for(int i=1;i<=n;i++){
     
		for(int j=1;j<=n;j++){
     
			scanf("%d",&edge[i][j]);//初始化邻接矩阵 
		}
	}
}
int prim(int v){
     
	int MIN,lowcost[maxn],closest[maxn],k,sum=0;
	for(int i=1;i<=n;i++){
     
		lowcost[i]=edge[v][i];
		closest[i]=v;
	}
	for(int i=1;i<n;i++){
     //贪心选择n-1个顶点加入 
		MIN=INF;
		for(int j=1;j<=n;j++){
     
			if(lowcost[j]&&lowcost[j]<MIN) MIN=lowcost[j],k=j;
		}
		lowcost[k]=0,sum+=MIN;
		for(int j=1;j<=n;j++){
     //修改侯选边边权和前驱结点 
			if(lowcost[j]&&edge[k][j]<lowcost[j]) lowcost[j]=edge[k][j],closest[j]=k;
		}
	}
	return sum;
}
int main(){
     
	cin>>n;
	init();
	cout<<prim(1);
	return 0;
}

Kuskal算法核心:与Prim算法相比有一些相似之处,都运用了贪心策略。它是一种按权值的递增次序选择合适的边来构造最小生成树的方法。对图中所有存在的边按递增排序,依次选择,若不能构成回路,表示可以选择,直到选完n-1条边。
并查集:用来判断该边并入后会不会产生环。
模板题:http://hihocoder.com/problemset/problem/1098

#include
#include
#include
using namespace std;
const int INF=0x3f3f3f3f,maxn=1e6+5;
int n,m,vset[maxn];
struct node{
     
	int u,v,w;
}a[maxn];
bool cmp(node x,node y){
     
	return x.w<y.w;
}
int find(int x){
     //递归寻找根结点 
	if(x==vset[x]) return x;
	else return vset[x]=find(vset[x]);
}
int kruskal(){
     
	int sum=0,s1,s2;
	sort(a,a+m,cmp);
	for(int i=0;i<n;i++) vset[i]=i;
	for(int i=0;i<m;i++){
      
		s1=find(a[i].u),s2=find(a[i].v);//分别得到两个顶点所属集合编号
		if(s1!=s2) vset[s1]=s2,sum+=a[i].w;//不会形成环,可加入 
	}
	return sum;
}
int main(){
     
	cin>>n>>m;
	for(int i=0;i<m;i++) scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);//初始化边集 
	cout<<kruskal();
	return 0;
}

你可能感兴趣的:(算法,图论,数据结构,c++)