两种做法 Kruscal 和 Prim
虽然这个适合稀疏图,但好像没怎么遇到边很多的图,就算边很多,因为是遍历一遍边 即O(n) 复杂度,但是因为要先排序 所以总的复杂度为 mlogm 也是很快速算完的,且经过对比 比Prim 耗时还要短
需要用sort对边的结构体进行排序
struct edge{
int u,v;
long long len;
}e[maxm];
bool cmp(edge &e1,edge &e2){
return e1.len < e2.len;
}
然后需要使用并查集 且需要路径压缩
(现在不怎么看的懂了 但是代码很简洁 就直接记下来吧)
int fa[maxn];
int find(int x)
{
return fa[x] == x? x : fa[x]=find(fa[x]);
}
下面就是 算法核心 每次选取最短的边 已经排好序
void Kruscal()
{
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++){
int tempu = e[i].u,tempv=e[i].v;
int fau = find(tempu),fav=find(tempv);
if(fau != fav){
ans +=e[i].len;
fa[fau] = fav;
t++;//记录树中边的数目
}
}
}
下面是源码
#include
using namespace std;
const int maxm = 400000+100;
const int maxn = 5000+100;
struct edge{
int u,v;
long long len;
}e[maxm];
bool cmp(edge &e1,edge &e2){
return e1.len < e2.len;
}
int fa[maxn];
long long ans=0;
int n,m,t;//t 是树中边的数目
int find(int x)
{
return fa[x] == x? x : fa[x]=find(fa[x]);
}
void Kruscal()
{
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++){
int tempu = e[i].u ,tempv = e[i].v;
int fau=find(tempu) ,fav=find(tempv);
if(fau!=fav){
ans +=e[i].len;
fa[fau] = fav;
t++;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].len);
sort(e+1,e+1+m,cmp);
Kruscal();
if(t == n-1)
printf("%lld",ans);
else
printf("orz");
return 0;
}
这个适合稠密图 即点少 边多
这里同样使用链式前向星存储 注意 最小生成树是无向图 存边时要存放两次
struct edge{
int to,next;
long long len;
}e[maxm];
int head[maxn];
int cnt=0;
void addedge(int from,int to,long long len){
cnt++;
e[cnt].to = to;
e[cnt].len = len;
e[cnt].next = head[from];
head[from] = cnt;
}
下面算法核心 依次求出点到这棵树的最小距离
void Prim()
{
int now = 1;//从第一个点开始
for(int i=2;i<=n;i++)
dis[i] = inf;
//初始化 dis[1]=0 其余为无穷
for(int i=head[1];i;i=e[i].next){
dis[e[i].to] = min(dis[e[i].to],e[i].len);
}//更新与第一个点相连的点到这棵树的距离
for(int i=1;i dis[j]){
minn = dis[j];
now = j;
}
}
ans +=minn;
for(int j=head[now];j;j=e[j].next){//更新距离
if(!vis[e[j].to] && dis[e[j].to]>e[j].len)
dis[e[j].to] = e[j].len;
}
}
}
下面是源码
#include
using namespace std;
const int maxn = 5000+100;
const int maxm = 400000+100;
const int inf = 100000000;
struct edge{
int to,next;
long long len;
}e[maxm];
int head[maxn];
bool vis[maxn];
long long dis[maxn];//点 到这棵树的距离
int n,m;
int cnt=0;
long long ans=0;
void addedge(int from,int to,long long len){
cnt++;
e[cnt].to = to;
e[cnt].len = len;
e[cnt].next = head[from];
head[from] = cnt;
}
void Prim()
{
int now = 1;//从第一个点开始
for(int i=2;i<=n;i++)
dis[i] = inf;
//初始化 dis[1]=0 其余为无穷
for(int i=head[1];i;i=e[i].next){
dis[e[i].to] = min(dis[e[i].to],e[i].len);
}//更新与第一个点相连的点到这棵树的距离
for(int i=1;i dis[j]){
minn = dis[j];
now = j;
}
}
ans +=minn;
for(int j=head[now];j;j=e[j].next){
if(!vis[e[j].to] && dis[e[j].to]>e[j].len)
dis[e[j].to] = e[j].len;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
int u,v;
long long len;
for(int i=1;i<=m;i++){
scanf("%d%d%lld",&u,&v,&len);
addedge(u,v,len);
addedge(v,u,len);
//最小生成树 在无向图中 要加两次
}
Prim();
printf("%lld",ans);
return 0;
}