生成树机制实验(1)Kruskal-2

该博客介绍了克鲁斯卡尔(Kruskal)算法在构建最小生成树问题中的应用。通过C语言实现,展示了如何对边按权重排序并检查是否形成环,以找到连接所有节点的最低成本树。同时,给出了一个示例程序,用于处理村庄之间的畅通工程问题。博客讨论了如何初始化节点、查找节点根、合并子树以及快速排序等关键步骤。
摘要由CSDN通过智能技术生成

生成树机制实验(1)Kruskal-2

畅通工程1863

Sample Input

3 3
1 2 1
1 3 2
2 3 4
1 3
2 3 2
0 100
具体实现
  • 注意输入输出,以及qsort()系统调用
# include <stdio.h>
# include <stdlib.h>
# define MAXN 10000 + 10

int par[MAXN],Rank[MAXN];
/*前一个 par 数组用来记录每一个结点的根结点,后一个 Rank[MAXN]用来记录所在子树的高度,
所在子树的高度指的是根结点所在子树的高度,由于优化手段之后,会将所有间接与根结点相连的部分与根结点直连,一般地,在合并时,只需要比较根结点*/
typedef struct{
    int a,b,price;
}Node;

Node a[MAXN];
/* typedef struct 定义一个结构体,具体用法为 typedef struct{}Node; Node 名称
Node 的 a,b 成员表示相连的两个村庄,price表示价格*/

void Init(int n){
    for(int i = 0; i < n; i++){
        Rank[i] = 0;
        par[i] = i;
    }
}

int cmp(const void*a,const void*b){
    return ((Node*)a)->price - ((Node*)b)->price;
}

int find(int x){
 	if(x == par[x])
        return x;
    return par[x] = find(par[x]); // 不仅返回,还对所有不直接连根的x上的点直接连根
}

void unite(int x,int y){
    x = find(x);
    y = find(y);
    if(Rank[x] < Rank[y]){
        par[x] = y;
    }
    else{
        par[y] = x;
        if(Rank[x]==Rank[y]) Rank[x]++;
    }
}

// 输入n : 边数,m : 村庄数(顶点数)
int Kruskal(int n,int m){
    int nEdge = 0,res = 0;
    /* nEdge 表示进入最小生成树边集合中边的数目,当 nEdge = 顶点数 - 1, 表示构造完成
    res 表示总成本*/
    // 快速排序,按边的权值大小
    qsort(a,n,sizeof(a[0]),cmp);
    /*指向数组的指针,元素个数,每个元素的大小,比较函数的指针*/
    for(int i = 0; i < n && nEdge != m - 1; i ++){
        // 开始判断每一条排完序后的 结构体数组 a 中的两个顶点是否属于同一棵树
        if(find(a[i].a) != find(a[i].b)){
            unite(a[i].a,a[i].b);
            //合并
            res += a[i].price;
            nEdge++;
        }
    }
    if(nEdge < m - 1) res = -1; // 无法构造
    return res;
}

int main(){
    int n,m,ans;
    // ans接收 Kruskal函数返回的结果
    while(scanf("%d%d",&n,&m),n){
    /*scanf对于 %d 类型的数据,空格表示结束,所以既可以写成 scanf("%d%d",&n,&m),也		可以写成 scanf("%d %d",&n,&m),两者等价
    逗号表达式看最后一个的结果,当 n = 0 时结束,符合题意中 n = 0 不需要输出*/
        Init(m);
        // m 个村庄,相关的数组为 par[MAXN] 和 Rank[MAXN]
        for(int i = 0; i < n; i++){
            scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].price);
            // 把村庄编号从1~n,变为0~n-1,也可以不改变
            a[i].a--;
            a[i].b--;
        }
        ans = Kruskal(n,m);
        if(ans = -1) printf("?\n");
        else printf("%d\n",ans);
    }
    return 0;
}

如何构建开销最小的生成树拓扑?

def Kruskal(graph):
    graph = sorted(graph)
# 定义图结构,并按照边的权值进行排序 sorted,这里仅展示 kruskal()函数
    parent,rank = [],[]
# 等价于par,第一个记录结点,第二个记录树的高度
    V = Len(graph.nodes())
# V: 表示图中顶点的个数
    for node in range(V):
        parent.append(node)
        rank.append(0)
# for循环对每个点初始化,自己是根,子树高度为0
     result = []
     i,e = 0,0
# 初始化,i 用来循环,e 表示最小生成树的边数
     while e < V - 1:
        u,v,w = graph[i]
# 遍历每一个边,寻找graph[i],第i个结构体成员的两个顶点和边
        i = i + 1
        x = find(parent,u)
# 两个顶点的根是什么?
        y = find(parent,v)
        
        if x!= y:
# 如果是不同子树,边数 + 1,结果列表添加元素
            e = e + 1
            result.append([u,v,w])
# 合并子树
            union(parent,rank,x,y)
        print("Edges in the constructed MST:")
        for u,v, weight in result:
# 遍历最后的最小生成树边表:最小值++,打印边信息
            minimumCost += weight
            print("%d -- %d,%d" %(u,v,weight))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值