hdu 1233 kruskal 算法 最小生成树(模板)

转载注明出处csdn bestsortkruskal和prim算法都是求最小生成树经常用到的算法,其中,prim适用于稠密图(边较多),而kruskal更适用于稀疏图(边较少)。其时间复杂度为O(elog2(e)).其中,e为图的边

 

ADCE是最短边,长度为5,AD第一个选择的边,被突出显示。

CE现在是不构成循环的第二条最短的边,长度为5,突出显示为第二个边。

下一条边,长度为6的DF,突出显示为第三条边。

接下来的最短边是ABBE,都是长度为7.随机选一条,这里我们选AB,并且突出显示。边缘BD以红色突出显示,因为在BD之间已经存在一条路径(绿色),所以如果选择它,它将形成一个循环(ABD)。





继续突出显示下一条最小的边,BE长度为7.许多个边缘以红色显示:BC,因为它会形成回路BCEDE,因为它会形成回路DEBAFE因为它会形成FEBAD

最后,该过程以长度为9 的边EG结束,并找到最小生成树。



 

kruskal和prim都是基于贪心的思想,其中,kruskal是每次将属于不同集合的最小的一条边合并。这样,一直合并下去,最终会得到一棵最小生成树(总点数一定,连接的点不断增多,所以未连接的点就会逐渐减少直到为0),需要注意的是,kruskal是基于并查集优化的,所以了解kruskal算法之前,应该先看一下并查集

 

1.使用结构体储存边的信息:

struct edge{
    int a,b;        //a为起点,b为终点
    int v;          //v为该边的权值
}s[maxn];///将结构体按权值排序后,依次进行合并操作

2.并查集合并及路径压缩:

 

int pre[maxn];    ///各集合

int Find(int a){        ///寻找祖宗节点的同时进行路径压缩
    int root = a;
    int tmp;
    while(root != pre[root])root = pre[root];
    while(a != root){
        tmp = a;
        a = pre[a];
        pre[tmp] = root;
    }
    return root;
}

void combine(int a,int b){    ///合并a,b两个集合,这里我们规定将较大的数并入到较小的数中
    int x = Find(a);
    int y = Find(b);
    if(x > y)
        swap(x,y);
    pre[y] = x;
}

3;kruskal主代码:

int kruscal(int n,int m){
    int ans = 0;
    For(i,m){
        if(Find(s[i].a)!=Find(s[i].b)){    ///如果a点所在的集合和b点所在的集合不相交
            combine(s[i].a,s[i].b);        ///合并两个集合
            ans += s[i].v;                
            n--;
        }
        if(n == 1)    ///当已经连了n-1条边时,就已经构成了最小生成树因为每n个点要连在一起只需要n-1条线即可
            break;
    }
    return ans;
}

例题:hdu1233

 

某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。 

Input测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。 
当N为0时,输入结束,该用例不被处理。 
Output对每个测试用例,在1行里输出最小的公路总长度。 
Sample Input

3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0

Sample Output

3
5


        
  
Huge input, scanf is recommended.

Hint

Hint
        
 

   

裸kruskal即可

参考代码:

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <cstring>
#define For(i,n) for(int i=0;i<n;i++)
#define mem(a) memset(a,0,sizeof(a))
using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 1e4+5;
typedef long long ll;

struct edge{
    int a,b;
    int v;
}s[maxn];

int pre[maxn];

bool cmp(edge a,edge b){return a.v < b.v;}
int Find(int a){
    int root = a;
    int tmp;
    while(root != pre[root])root = pre[root];
    while(a != root){
        tmp = a;
        a = pre[a];
        pre[tmp] = root;
    }
    return root;
}

void combine(int a,int b){
    int x = Find(a);
    int y = Find(b);
    if(x > y)
        swap(x,y);
    pre[y] = x;
}

int kruscal(int n,int m){
    int ans = 0;
    For(i,m){
        if(Find(s[i].a)!=Find(s[i].b)){
            combine(s[i].a,s[i].b);
            ans += s[i].v;
            n--;
        }
        if(n == 1)
            break;
    }
    cout << ans <<endl;


}
int main() {
    int n;
    int a,b,c;
    int cnt;
    while(cin >> n && n){
        mem(s);
        For(i,n+1)
            pre[i] = i;
        cnt = 0;
        For(j,n*(n-1)/2){
            cin >> a >> b>>c ;
            s[cnt].a = a;
            s[cnt].b = b;
            s[cnt++].v = c;
        }
        sort(s,s+cnt,cmp);            ///将边的权值按从小到大排序
        kruscal(n,n*(n-1)/2);         ///一共n个点,n*(n-1)/2条边
    }


    return 0;
    }

 

 

 

转载于:https://www.cnblogs.com/bestsort/p/10588879.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值