一、定义
用大白话来解释就是,用一个图把所有的点都连接起来,而且所有边的总权值在所有图中最小。
二、算法
(1)克鲁斯卡尔算法
基本思想:
第一步:把所有的边从小到大排序。
第二步:开始选边,每一个边要保证都不相连,即不能成圈。
第三步:判断一下,是不是选了n-1条边,因为当不成全的边数为n-1的时候,定可以把一张图连起来。
用大白话说:
先排序再判圈
难点:判断如何成圈?需要用到并查集中的根节点,如果两个点相连可以构成圈的话,那它们两个的根节点相同
拿这张图中的点为例,如果v1与v4相连会成圈,v4的根节点与v1的根节点绝对相同(都是v4或者v1)。
所以,不会成圈的前提就是两个点的根节点不同即可。
(2)以例题为例,详情看注释
HUD1863畅通工程
省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。经过调查评估,得到的统计表中列出了有可能建设公路的若干条道路的成本。现请你编写程序,计算出全省畅通需要的最低成本。
Input
测试输入包含若干测试用例。每个测试用例的第1行给出评估的道路条数 N、村庄数目M ( < 100 );随后的 N
行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。
Output
对每个测试用例,在1行里输出全省畅通需要的最低成本。若统计数据不足以保证畅通,则输出“?”。
Sample Input
3 3
1 2 1
1 3 2
2 3 4
1 3
2 3 2
0 100
Sample Output
3
?
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <sstream>
#include <iostream>
#include <cstring>
typedef unsigned long long ll;
const int INF=1e9;
const int maxn=1e6+5;
using namespace std;
int dis[maxn];
int pre[maxn];
struct node{
int x,y,len;
}edge[maxn];
int Find(int x)//寻找根节点
{
return x==pre[x]?x:Find(pre[x]);
}
bool Merge(int x,int y)
{
int x1=Find(x);
int y1=Find(y);
if(x1!=y1)判断根节点是否相同
{
pre[x1]=y1;//不相同之后将他们连接起来
return true;
}
return false;
}
bool cmp(node a,node b)
{
return a.len<b.len;
}
void restart()
{
for(int i=0;i<maxn;i++)
pre[i]=i;//并查集初始化
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)&&n)
{
restart();
for(int i=1;i<=n;i++)
scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].len);
sort(edge+1,edge+1+n,cmp);//先排序
int ans=0;
int paid=0;
for(int i=1;i<=n&&ans<=m-1;i++)//挨个遍历
{
if(Merge(edge[i].x,edge[i].y))//再判圈
{
ans++;
paid+=edge[i].len;
}
}
if(ans<m-1) printf("?\n");
else printf("%d\n",paid);
}
return 0;
}
(2)prim算法
基本思想:
第一步:指定一个起点开始,一般从1开始。
第二步:用擂台法比较出最小的那个边,并起点移动到那个点。
第三步:重复上面前两步。
用大白话表述:与迪杰斯特拉的求最短路的算法类似,只不过将判断条件改变了。
成立条件:有一个点没有联通到【dis[i]=INF】即没有形成最小生成树
还是以上面题为例,详情看代码:
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <sstream>
#include <iostream>
#include <cstring>
typedef unsigned long long ll;
const int INF=1e9;
const int maxn=1e6+5;
using namespace std;
int dis[maxn];
int pre[maxn];
struct node{
int x,y,len;
}edge[maxn];
int head[maxn];
int vis[maxn];
ll cnt=0;
ll paid=0;
void restart()
{
memset(head,-1,sizeof(head));
cnt=0;paid=0;
}
void addedge(int u,int v,int w)
{
edge[cnt].y=v;
edge[cnt].len=w;
edge[cnt].x=head[u];
head[u]=cnt++;
}
void prim(int n)//与dijkstra的算法类似。
{
for(int i=1;i<=n;i++) dis[i]=(i==1?0:INF),vis[i]=0;
while(2)
{
int k=-1,len=INF;
for(int i=1;i<=n;i++)//选择
{
if(!vis[i]&&len>dis[i])
{
k=i;
len=dis[i];
}
}
vis[k]=1;
if(k==-1) break;
for(int i=head[k];i!=-1;i=edge[i].x)//更新
{
int e=edge[i].y;
if(dis[e]>edge[i].len)
{
dis[e]=edge[i].len;
paid+=edge[i].len;
}
}
}
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)&&n)
{
restart();
for(int i=1;i<=n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
}
prim(m);
int flag=0;
for(int i=1;i<=m;i++)//判断是否生成了最小生成树
if(dis[i]==INF)
{
flag=1;
break;
}
if(flag) puts("?");
else printf("%d\n",paid);
}
return 0;
}
三、总结
1.这是求最小生成树最长用的两种算法,但不排除也会有变式。
2.克鲁斯卡尔算法,是以边为基准,而prim是以点为基准。
3.有意见可以交流,一起加油!