首页 > 其他分享 >最小生成树

最小生成树

时间:2024-01-28 17:11:55浏览次数:18  
标签:dist 加入 int 最小 生成 集合 include 号点

最小生成树

概念

给定一个无向图,在图中选择若干条边把图的所有的节点连接起来,要求边长之和最小。在图论中叫做最小生成树。

Prim算法

Prim算法生成最小生成树的过程基于贪心思想,每次将距离已经连通部分最近的点和对应的边加入连通部分,使得连通部分逐渐扩大,最后将整个图连接起来并且边长之和最小。

Prim(朴素版)

适用于稠密图,时间复杂度为\(O(n^2)\)

先把所有距离初始化为正无穷
for i : 0~n
	t = 集合S外距离最近的点
	//**与Dijkstra算法不同的地方**
	//Dijkstra算法是用t更新其他点到 起点 的距离
	用 t 更新其他点到 集合 的距离

例如,当b、c、d都被加入到集合s中时,a此时到集合的距离表示为\(min(d1, d2, d3)\)

如果与a相连的点都没有被加入到集合s中,那么a此时到集合的距离表示为\(+∞\)

对于上图,Prim算法的过程为

1)先加入1号点

2)用1号点来更新与之相连的其他点

dis[2] = 1, dis[3] = 2, dis[4] = 7;

3)从当前dis[]数组中找到距离最小的点,也就是2,因此加入2号点到集合中,同时连边1——2

4)用2号点来更新与之相连的其他点(无更优值则无更新)

5)加入3号点到集合中,同时连边1——3

6)用3号点来更新与之相连的其他点

dis[4] = 3

7)加入3号点到集合中,同时连边3——4

8)连了\(n-1\)条边,程序结束

对应的最小生成树:

代码实现

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 5005;
int ans = 0;
int n, m;
int g[N][N], dist[N];
//flag数组表示当前点是否被加入到集合s中
bool flag[N];

void prim(){
    memset(dist, 0x3f, sizeof(dist));
    dist[1] = 0;
    flag[1] = 1;
    //t代表当前加入的结点
    int t = 1;
    int cs = n-1;
    while(cs--){
        //用t更新其他点到集合的距离
        for(int i = 1; i <= n; i++)
            if(!flag[i] && g[t][i] && dist[i] > g[t][i])    dist[i] = g[t][i];
        //选择距离集合最近的点加入到集合中,并且连边
        int minn = 0x3f3f3f3f, idx = 0;
        for(int i = 1; i <= n; i++){
            if(!flag[i] && minn > dist[i]){
                idx = i;
                minn = dist[i];
            }
        }
        if(minn == 0x3f3f3f3f){
            cout << "orz" << endl;
            return;
        }
        flag[idx] = 1;
        t = idx;
        ans += minn;
    }
    cout << ans << endl;
}

int main(){
    int x, y, z;
    cin >> n >> m;
    while(m --){
        cin >> x >> y >> z;
        if(!g[x][y]){
            g[x][y] = z;
            g[y][x] = z;
        }
        else{
            if(g[x][y] > z){
                g[x][y] = z;
                g[y][x] = z;
            }
        }
    }
    prim();
    return 0;
}

Prim(堆优化版)

适用于稀疏图(但是不常用,通常稀疏图更加倾向用Kruskal算法),时间复杂度为\(O(mlogn)\)

Kruskal算法

时间复杂度为\(O(mlogm)\)

算法思路

  1. 按照边的权值将边进行升序排序;\(O(mlogm)\)
  2. 从小到大枚举每条边,如果这个边与之前选择的所有边不会组成回路(边a——b,a和b如果是不连通的),就把这条边加入到集合s中;\(O(m)\)
  3. 不断判断,直到具有n个顶点的联通网筛选出来n-1条边为止。

此时筛选出来的边和所有的顶点构成此连通网的最小生成树。

对于判断是否会产生回路,我们使用并查集。

  • 在初始状态下,各个顶点在不同的集合中
  • 遍历每条边的时候,判断该边的两个顶点是否在一个集合中
  • 如果边上的这两个顶点在一个集合中,说明两个顶点一定已经连通,那么这条边加上去一定导致环,所以舍去该边。反之如果不在一个集合中,就加入这条边。

代码实现

#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100005, M = 200005;
int fa[N];
struct Edge{
    int u, v, w;
}edge[M];

bool cmp(Edge a, Edge b){
    return a.w < b.w;
}

int find(int x){
    if(fa[x] != x)   fa[x] = find(fa[x]);
    return fa[x];
}

int main(){
    int n, m, ans = 0;
    int u, v, w;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)     fa[i] = i;
    for(int i = 1; i <= m; i++){
        cin >> u >> v >> w;
        edge[i].u = u;
        edge[i].v = v;
        edge[i].w = w;
    }
    sort(edge+1, edge+m+1, cmp);
    int cnt = 0;
    for(int i = 1; i <= m && cnt < n-1; i++){
        //如果边连接的两个点是不连通的
        if(find(edge[i].u) != find(edge[i].v)){
            fa[find(edge[i].u)] = find(edge[i].v);
            ans += edge[i].w;
            cnt ++;
        }
    }
    if(cnt == n-1)      cout << ans << endl;
    else    cout << "impossible" << endl;
    return 0;
}

标签:dist,加入,int,最小,生成,集合,include,号点
From: https://www.cnblogs.com/hsy2093/p/17993019

相关文章

  • 生成一个满二叉数算法
    1、树结构类publicclassTreeNode<T>{Tval;TreeNode<T>parent;TreeNode<T>right;TreeNode<T>left;publicTreeNode(){}publicTreeNode(Tval){this.val=val;this.parent=nul......
  • 1,2,3……n的所有数的最小公倍数?
    importmathdeflcm(a,b):returna*b//math.gcd(a,b)deflcm_range(n):lcm_value=1foriinrange(2,n+1):lcm_value=lcm(lcm_value,i)returnlcm_valuen=81#输入给定的数值nresult=lcm_range(n)......
  • 揭秘 Wasitai:AI 图像生成检测利器
    引言Wasitai是一款强大的工具,它允许用户检查一张图片是否由机器生成。用户只需拖拽并放置一张图片或从设备中选择一张,该工具将对图像进行处理,以确定它是由人还是机器生成的。Wasitai的功能1.图像生成检测:Wasitai主要功能是检测图像的生成方式,判断其是否由人工智能或机器生......
  • 最小表示法&Manacher学习笔记+杂题
    字符串系列前言:孩子从小就自卑。四、最小表示法&Manacher学习笔记+杂题相关题单:戳我1.最小表示法最小表示法是用于解决字符串最小表示问题的方法。(1)字符串的最小表示:字符串\(s\)的最小表示为与\(s\)循环同构的所有字符串中字典序最小的字符串。循环同构指的是当字符......
  • 最小表示法&Manacher学习笔记+杂题
    字符串系列前言:孩子从小就自卑。四、最小表示法&Manacher学习笔记+杂题相关题单:戳我1.最小表示法最小表示法是用于解决字符串最小表示问题的方法。(1)字符串的最小表示:字符串\(s\)的最小表示为与\(s\)循环同构的所有字符串中字典序最小的字符串。循环同构指的是当字符......
  • 生产环境出现 bug 自动生成异常追踪-SRE与开发自动化协同
    作者:观测云数据智能部产品方案架构师范莹莹简介生产环境bug的定义:RUM应用和APM应用的 error_stack 信息被捕捉后成为bug。以APM新增错误巡检为例,当出现新错误时,在观测云控制台的「事件」模块下生成新的事件报告,捕捉为bug。同时利用 DatafluxFunc 创建异常追踪,......
  • [office] MAX、MIN与IF结合,统计众多部门中同一部门数据最大值与最小值
    一位做电商数据分析的朋友说,他要对所管理的六个仓库的销售额进行对比统计,统计出每个仓库的最高与最低销售额。他有几万行的数据,简化到下面几行,以方便清楚统计公式。公式实现在E2单元格输入公式:“=MAX(IF($A$2:$A$16=D2,$B$2:$B$16))”,按“Ctrl+Shift+Enter”结束,向下填充,即可计算出......
  • Shell脚本生成随机整数
     Shell脚本生成随机整数$RANDOM:使用当前的进程ID(PID)和当前时间/日期生成的,该时间/日期由自1970年以来经过的秒数定义。 1、urandom命令grep-m1-ao'[0-9]'/dev/urandom|seds/0/10/|head-n1 2、用$RANDOM 要生成范围:{0,..,9} r=$(($RANDOM%10))echo......
  • yxyx:verilator生成波形和gtkwave查看波形
    在verilator指令的末尾要加上--trace选项,会在obj文件夹里生成对应的xxx_vcd_c.d和.o文件。在上一部分里,c语言代码里只写了逻辑部分,但是想要gtkwave查看波形,还需要在代码里增加vcd指针并记录波形。新的代码如下:#include<stdio.h>#include<stdlib.h>#include<assert.h>#i......
  • 为了生成latex如何在sympy中自定义向量函数?适用于自定义类的latex生成。
    在sympy.printing.Printer的_print函数中可以看到一个hook,使得对于每一个类都会尝试寻找对应的_print_{class}函数来处理,因此我们只要利用好这个hook就可以为自定义类创建latex生成逻辑,我试图创建了一个_print_BoldUndefinedFunction函数,但发现它捕获不到(其实是因为BoldUndefinedF......