<pre name="code" class="cpp">/*
题意:给你一个n*n的连通图,求将他全部连起来的最小花费,即求其最小生成树
这里我们用到 Kruskal算法 和并查集算法
Kruskal算法简述
假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有 n 棵树的一个森林。之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。
并查集算法简述
并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并。
对于解题方法,代码中写得很详细。
*/
#include<stdio.h>
#include<algorithm>
using namespace std;
struct edge{
int s,e,v;
}e[5003];//保存所有的边,s起点,e终点,v花费
bool cmp(edge x,edge y)//按边长从小到大排序
{
return x.v<y.v;
}
struct tree{
int p;//parent
int s;//sum
}t[102];//并查集和,最终必有n个元素,p父亲节点,s个数
int n,val,num,ans;
void init()//初始化并查集和,令根为本身,集合个数为1
{
for(int i=0;i<n;i++)
{
t[i].p=i;
t[i].s=1;
}
}
int find(int x)//找到x的根节点
{
while(x!=t[x].p)
{
t[x].p=t[t[x].p].p;
x=t[x].p;
}
return x;
}
void uion(int sr,int er)//合并以sr为根的集合(a)和以er为根的集合(b)
{
if(t[sr].s>t[er].s)//深度的优化,如果a大于b,那么将b合并到a,反之则反
{
t[er].p=sr;//修改b的根
t[sr].s+=t[er].s;//将b的大小加到a上
}else{
t[sr].p=er;
t[er].s+=t[sr].s;
}
}
void kruskal()
{
init();
for(int i=0;i<num;i++)
{
int sr=find(e[i].s);//起点的根
int er=find(e[i].e);//终点的根
if(sr!=er)//判断这两个点是否已经连接
{
uion(sr,er);//合并这两个点
ans+=e[i].v;//加上这两边的长度(花费),因为已经排序所以一定是最短的
}
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
num=0;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
scanf("%d",&val);
if(i>j)//无向图取一半即可
{
e[num].s=i;
e[num].e=j;
e[num++].v=val;
}
}
ans=0;//初始化最小花费
sort(e,e+num,cmp);//按边长从小到大排序
kruskal();
printf("%d\n",ans);
}
}
poj1258_kruskal
最新推荐文章于 2024-07-22 23:59:01 发布