kruskal算法
求加权连通图的最小生成树的算法。
Kruskal算法的第一步是给所有边按照从大到小的顺序排列。这一步可以直接使用库函数qsort或者sort.接下来从小到大一次考察每个边(u,v)。
情况1:u和v在同一个连通分量中,那么加入(u,v)后会形成环,因此不能选择。
情况2:如果u和v在不同的连通分量,那么加入(u,v)一定是最优的。
与并查集的结合使问题得以解决
UVA1395 苗条的生成树 Slim Span
题意翻译
题意简述
求所有生成树中最大边权与最小边权差最小的,输出它们的差值。
输入:
输入文件包含多组测试数据,每组测试数据如下: 第1行:2个整数n m (2 ≤ n ≤ 100 and 0 ≤ m ≤ n(n − 1)/2),n表示顶点数,m表示边数。接下来m行,每行3个空格分开的整数a b w(1 ≤ w ≤ 10000) , 表示顶点a与顶点b之间有一条边,权值为w。
输出:
对每组测试数据,如果图存在生成树,输出生成树的差值最小的;否则,输出-1。
样例输入:
4 5
1 2 3
1 3 5
1 4 6
2 4 6
3 4 7
4 6
1 2 10
1 3 100
1 4 90
2 3 20
2 4 80
3 4 40
2 1
1 2 1
3 0
3 1
1 2 1
3 3
1 2 2
2 3 5
1 3 6
5 10
1 2 110
1 3 120
1 4 130
1 5 120
2 3 110
2 4 120
2 5 130
3 4 120
3 5 110
4 5 120
5 10
1 2 9384
1 3 887
1 4 2778
1 5 6916
2 3 7794
2 4 8336
2 5 5387
3 4 493
3 5 6650
4 5 1422
5 8
1 2 1
2 3 100
3 4 100
4 5 100
1 5 50
2 5 50
3 5 50
4 1 150
0 0
样例输出
1
20
0
-1
-1
1
0
1686
50
思路:
第一步:建一个结构体把边的两个端点和边的权值存入结构体;
第二步:根据每条边权值大小进行sort从小到大排序
第三步:遍历每一条边,对每一个符合条件的边(不会成环)记录下每一次的差值并储存到ans变量中
Find函数和Merge函数是并查集的基本用法,不同的是在Merge函数里 对判断不在同一集合的两个点的返回值为1
kruskal函数中先对pri[i]数组赋初值(pri[i]数组存储的是 i 点的根节点是谁),对k之后的每一个点进行判断并更新最大边的权值存入xmax中(因为开始已经对边的权值从小到大排过序了)
直到找到最后一个点(如果没有到最后一个点,返回值是0,ans值不会改变,就输出-1,否则一直更新ans为最小差值,最后输出)
完整代码
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<math.h>
typedef long long ll;
const int xmax2=1e5+7;
const int INF=1e9+7;
using namespace std;
int n,m,xmax,ans,cnt;
struct node{
int u,v,w; ///两个端点和边权值
}edge[xmax2];
int pre[xmax2]; ///存储根节点
int Find(int x) ///查找函数
{
if(pre[x]==x)
return x;
else
return pre[x]=Find(pre[x]); ///路径压缩
}
int Merge(int x,int y)
{
int fx=Find(x);
int fy=Find(y);
if(fx!=fy)
{
pre[fx]=fy;
return 1; ///不在一个集合里返回1
}
return 0;
}
int cmp(node x,node y)
{
return x.w<y.w; ///结构体排序,从小到大
}
int kruskal(int k)
{
xmax=-1;
cnt=0; ///记录
for(int j=1;j<=n;j++)
pre[j]=j; ///赋初值
for(int i=k;i<=m;i++)
{
if(Merge(edge[i].u,edge[i].v)){
///如果满足不在一个集合里,更新xmax
xmax=max(xmax,edge[i].w);
cnt++;
if(cnt==n-1)
return 1; ///到了最后一个点返回1
}
}
return 0;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF&&(n||m))
{
for(int i=1;i<=m;i++)
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
sort(edge+1,edge+m+1,cmp);
ans=INF; ///对ans赋初值为一个极大值
for(int i=1;i<=m;i++)
{
if(kruskal(i)) ///满足条件就更新ans的值,否则ans值不变
ans=min(ans,xmax-edge[i].w);
}
if(ans==INF) ans=-1;
printf("%d\n",ans);
}
return 0;
}