本篇纯属鬼扯,因为不是很想看大神们写的Prim算法和Kruskal算法,所以靠自己理解写一下这两天刷的最小生成树的题目,希望以后能把本篇修正。
Kruskal算法
一、定义:
最小生成树是指通过算法找到搜索到的边子集所构成的树中,不但包括了加权连通图里的所有顶点 (英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。
二、概述:
设G=(V,E)是一个加权连通图,首先定义一个结构体数组struct line r[maxm]存放节点之间的权值,然后将结构体数组按权值从小到大排序,依次取出r[i]比较,如果r[i]中节点已经加到树中,则看下一个,如果还未加入,则将未加入的点放入树中,不必按大小顺序,直至树中边集达到n-1为止,n为节点总数。这样求出的树一定是最小生成树,这也是贪心思想的一种体现。
三、步骤:
1.定义结构体struct line {int a,b,c}r[maxm];并按题意赋值
2.将结构体数组按权值从小到大排序
3.定义pre[maxn]数组,用来记录父节点,并初始化pre[i]=i
4.定义数组Find(int x),用来找到与x节点连通的最前面的父节点,就好像找到最大的boss,不一定直接相连,只要连通的就行
4.建立循环,i从1…m,如果Find(r[i].a)!=Find(r[i].b),就令pre[Find(r[i].a)]=Find(r[i].b),即令Find(r[i].b)做为Find(r[i].a)的父节点。也可以用tot < n-1作为控制循环结束的条件,tot 为边数
5.循环结束形成的树为最小生成树,只是按此算法每一层次并无明显大小关系
五、附图:
六、核心代码
自己代码:
#define INF 99999999
int pre[maxn];
struct line
{
int a,b,c;
}r[maxm];
int Find(int x)
{
return pre[x]==x?x:Find(pre[x]);
}
bool cmp(struct line x,struct line y)
{
return x.c<y.c;
}
void Kruskal()
{
int i;
for (i=1 ;i<=n;i++)
pre[i]=i;
sort(r+1 ,r+m+1 ,cmp);
for (i=1 ;i<=m;i++)
{
if (Find(r[i].a)!=Find(r[i].b))
{
pre[Find(r[i].a)]=Find(r[i].b);
}
}
}
大神代码:
using namespace std ;
#define INF 99999999
int pre[maxn];
struct line
{
int a,b,c;
}r[maxm];
int Find(int x)
{
return pre[x]==x?x:Find(pre[x]);
}
bool cmp(struct line x,struct line y)
{
return x.c<y.c;
}
void kruskal()
{
int i;
for (i=1 ;i<=n;i++)
pre[i]=i;
sort(r+1 ,r+m+1 ,cmp);
int tot=0 ;
i=1 ;
while (tot<n-1 )
{
while (i<=m&&Find(r[i].a)==Find(r[i].b)) i++;
if (i>m) break ;
pre[Find(r[i].a)]=Find(r[i].b);
tot++;
}
return 0 ;
}
七、缺点:
图的存贮结构采用边集数组,且权值相等的边在数组中排列次序可以是任意的.该方法对于边相对比较多的不是很实用,浪费时间.
八、例题
题目概述:求出Bessie所要带的最多的水,因为她每到一个农场都可以补充水,所以就是求最小生成树的最大边
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std ;
#define INF 99999999
int pre[2005 ];
int Find(int x)
{
return pre[x]==x?x:Find(pre[x]);
}
struct line
{
int a,b,c;
}r[10005 ];
bool cmp(struct line x,struct line y)
{
return x.c<y.c;
}
int main()
{
int n,m;
int i;
scanf ("%d%d" ,&n,&m);
for (i=1 ;i<=n;i++)
pre[i]=i;
for (i=1 ;i<=m;i++)
{
scanf ("%d%d%d" ,&r[i].a,&r[i].b,&r[i].c);
}
sort(r+1 ,r+m+1 ,cmp);
int maxline=0 ;
int tot=0 ;
i=1 ;
while (tot<n-1 )
{
while (i<=m&&Find(r[i].a)==Find(r[i].b)) i++;
if (i>m) break ;
pre[Find(r[i].a)]=Find(r[i].b);
if (maxline<r[i].c)
maxline=r[i].c;
tot++;
}
printf ("%d\n" ,maxline);
return 0 ;
}
题目概述:全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小,很简单的一个最小生成树题目
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std ;
int pre[5000 ];
struct line
{
int a,b,c;
}r[5000 ];
bool cmp(struct line x,struct line y)
{
return x.c<y.c;
}
int Find(int a)
{
return pre[a]==a?a:Find(pre[a]);
}
int main()
{
int n,m,i,j,l;
while (scanf ("%d" ,&n)!=EOF&&n)
{
for (i=1 ;i<=n;i++)
pre[i]=i;
m=n*(n-1 )/2 ;
for (i=1 ;i<=m;i++)
{
scanf ("%d%d%d" ,&r[i].a,&r[i].b,&r[i].c);
}
sort(r+1 ,r+m+1 ,cmp);
l=0 ;
for (i=1 ;i<=m;i++)
{
if (Find(r[i].a)==Find(r[i].b))
continue ;
else if (Find(r[i].a)>Find(r[i].b))
{
l+=r[i].c;
pre[Find(r[i].a)]=Find(r[i].b);
}
else
{
l+=r[i].c;
pre[Find(r[i].b)]=Find(r[i].a);
}
}
printf ("%d\n" , l);
}
return 0 ;
}
题目概述:求让每个中心枢纽相通的最小生成树,输出最小生成树最大边,以及最小生成树的边数和边集
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std ;
#define INF 99999999
#define maxm 15005
#define maxn 1005
struct line
{
int a,b,c;
}r[maxm];
int pre[maxn];
bool s[maxm]={0 };
int n,m;
int Find(int x)
{
return pre[x]==x?x:Find(pre[x]);
}
bool cmp(struct line x,struct line y)
{
return x.c<y.c;
}
int main()
{
int i;
scanf ("%d%d" ,&n,&m);
for (i=1 ;i<=m;i++)
{
scanf ("%d%d%d" ,&r[i].a,&r[i].b,&r[i].c);
}
for (i=1 ;i<=n;i++)
{
pre[i]=i;
}
sort(r+1 ,r+m+1 ,cmp);
int maxline=0 ;
int ans=0 ;
for (i=1 ;i<=m;i++)
{
if (Find(r[i].a)!=Find(r[i].b))
{
pre[Find(r[i].a)]=Find(r[i].b);
ans++;
s[i]=1 ;
if (maxline<r[i].c)
maxline=r[i].c;
}
}
printf ("%d\n%d\n" ,maxline,ans);
for (i=1 ;i<=m;i++)
{
if (s[i])
printf ("%d %d\n" ,r[i].a,r[i].b);
}
return 0 ;
}