模板题目
题目描述
如题,给出一个无向图,求出最小生成树
输入输出格式
输入格式:
第一行包含两个整数N、M,表示该图共有N个结点和M条无向边。(N<=5000,M<=200000)
接下来M行每行包含三个整数Xi、Yi、Zi,表示有一条长度为Zi的无向边连接结点Xi、Yi
输出格式:
输出包含一个数,即最小生成树的各边的长度之和
输入输出样例
输入样例#1:
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
输出样例#1:
7
说明
时空限制:1000ms,128M
数据规模:
对于20%的数据:N<=5,M<=20
对于40%的数据:N<=50,M<=2500
对于70%的数据:N<=500,M<=10000
对于100%的数据:N<=5000,M<=200000
所以最小生成树的总边权为2+2+3=7
分析
这道题目呢,在入门的时候讲过prim算法。这里对于prim算法进行简单的介绍。prim算法,就是把所有的点划在A集合里面,然后每次选择一个点到B集合(满足AB结点的交叉边保持最小)然后边数达到n-1的时候就成功了。
克鲁斯卡尔
克鲁斯卡尔就是基于并查集的。把每个点都看成一个集合,然后对每条边进行按权值从小到大排序,如果当前边的两个端点不在一个集合里面(查),那就,并他;在一个集合里面就,不要他。(哈哈哈还是我讲的贪心算法的应用)
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,cc=0,sum=0;
struct edge
{
int l,r,v;
}a[200200];
//用来存放所有的边,l和r是两个端点,v是权值
int top;
int father[6000];
//用来存放所有结点的父亲
void read()
{
scanf("%d%d",&n,&m);
int x,y,z;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
a[i].l=x;
a[i].r=y;
a[i].v=z;
}
for(int i=1;i<=n;i++)
father[i]=i;
}
//读入和初始化
bool mycmp(edge x,edge y)
{
return x.v<y.v;
}
int getfather(int v)
{
if(father[v]==v) return v;
father[v]=getfather(father[v]);
return father[v];
}
//获取根结点
void un(int x,int y)
{
int fx,fy;
fx=getfather(x);
fy=getfather(y);
father[fx]=fy;
}
//并集
bool judge(int x,int y)
{
int fx,fy;
fx=getfather(x);
fy=getfather(y);
return fx!=fy;
}
//查点
void work()
{
for(int i=1;i<=m&&cc<=n-1;i++)
//cc存储已经拥有的边数。最多n-1条
if(judge(a[i].l,a[i].r))
//如果没有在一个结点里面。
{
un(a[i].l,a[i].r);
//并
cc++;
//计数
sum+=a[i].v;
//累加权值和
}
}
int main()
{
read();
sort(a+1,a+1+m,mycmp);
//按权值排序
work();
printf("%d",sum);
return 0;
}