NO.92十六届蓝桥杯备战|图论基础-最小生成树-Prim算法-Kruskal算法|买礼物|繁忙的都市|滑雪(C++)

news2025/7/14 11:48:47

一个具有n个顶点的连通图,其⽣成树为包含n-1条边和所有顶点的极⼩连通⼦图。对于⽣成树来说,若砍去⼀条边就会使图不连通图;若增加⼀条边就会形成回路。
![[Pasted image 20250414153851.png]]

![[Pasted image 20250414153858.png]]

⼀个图的⽣成树可能有多个,将所有⽣成树中权值之和最⼩的树称为最⼩⽣成树。
构造最⼩⽣成树有多种算法,典型的有普利姆(Prim)算法和克鲁斯卡尔(Kruskal)算法两种,它们都是基于贪⼼的策略。

Prim算法

核⼼:不断加点。
Prim 算法构造最⼩⽣成树的基本思想:

  1. 从任意⼀个点开始构造最⼩⽣成树;
  2. 将距离该树权值最⼩且不在树中的顶点,加⼊到⽣成树中。然后更新与该点相连的点到⽣成树的最短距离;
  3. 重复2操作n次,直到所有顶点都加⼊为⽌
    ![[Pasted image 20250414164712.png]]
1

1 - 5

1 - 5 - 2

1 - 5 - 2
1 - 4

1 - 5 - 2 - 3
1 - 4
P3366 【模板】最小生成树 - 洛谷

代码实现-邻接矩阵:

#include <bits/stdc++.h>
using namespace std;

const int N = 5010, INF = 0x3f3f3f3f;

int n, m;
int edges[N][N]; //邻接矩阵

int dist[N]; //某个点距离生成树的最短距离
bool st[N]; //标记哪些点已经加入到生成树

int prim()
{
    //初始化
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    int ret = 0;
    for (int i = 1; i <= n; i++) //循环加入n个点
    {
        //1.找最近点
        int t = 0;
        for (int j = 1; j <= n; j++)
            if (!st[j] && dist[j] < dist[t])
                t = j;
        //判断是否连通
        if (dist[t] == INF) return INF;
        st[t] = true;
        ret += dist[t];
        
        //2.更新距离
        for (int j = 1; j <= n; j++) //枚举t能走到哪
        {
            dist[j] = min(dist[j], edges[t][j]);
        }
    }
    return ret;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m;
    //初始化为无穷
    memset(edges, 0x3f, sizeof edges);
    
    for (int i = 1; i <= m; i++)
    {
        int x, y, z; cin >> x >> y >> z;
        //有重边求最小值
        edges[x][y] = edges[y][x] = min(edges[x][y], z);
    }

    int ret = prim();
    if (ret == INF) cout << "orz" << endl;
    else cout << ret << endl;
    
    return 0;
}

代码实现-邻接表-vector数组:

#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> PII;

const int N = 5010, INF = 0x3f3f3f3f;

int n, m;
vector<PII> edges[N];

int dist[N];
bool st[N];

int prim()
{
    memset (dist, 0x3f, sizeof dist);
    dist[1] = 0;
    int ret = 0;
    
    for (int i = 1; i <= n; i++)
    {
        //1.找最近点
        int t = 0;
        for (int j = 1; j <= n; j++)
            if (!st[j] && dist[j] < dist[t])
                t = j;
        //判断是否连通
        if (dist[t] == INF) return INF;
        st[t] = true;
        ret += dist[t];
        //2.更新距离
        for (auto& p : edges[t])
        {
            int a = p.first, b = p.second;
            //t->a,权值是b
            dist[a] = min(dist[a], b);
        }
    }
    return  ret;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int x, y, z; cin >> x >> y >> z;
        edges[x].push_back({y,z});
        edges[y].push_back({x,z});
    }
    int ret = prim();
    if (ret == INF) cout << "orz" << endl;
    else cout << ret << endl;
    
    return 0;
}
Kruskal算法

核⼼:不断加边。
Kruskal 算法构造最⼩⽣成树的基本思想:

  1. 所有边按照权值排序;
  2. 每次选出权值最⼩且两端顶点不连通的⼀条边,直到所有顶点都联通
    ![[Pasted image 20250414201240.png]]
1 - 5

1 - 5 - 2

1 - 5 - 2
1 - 4

1 - 5 - 2 - 3
1 - 4
#include <bits/stdc++.h>
using namespace std;

const int N = 5010, M = 2e5 + 10, INF = 0x3f3f3f3f;

int n, m;
struct node
{
    int x, y, z;
}a[M];

int fa[N]; //并查集

bool cmp(node& a, node& b)
{
    return a.z < b.z;
}

int find(int x)
{
    return x == fa[x] ? fa[x] : fa[x] = find(fa[x]);
}

int kk()
{
    sort (a+1, a+1+m, cmp);

    int cnt = 0;
    int ret = 0;
    for (int i = 1; i <= m; i++)
    {
        int x = a[i].x, y = a[i].y, z = a[i].z;

        int fx = find(x), fy = find(y);
         if (fx != fy)
         {
             cnt++;
             ret += z;
             fa[fx] = fy;
         }
    }
    return cnt == n-1 ? ret : INF;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m;
    for (int i = 1; i <= m; i++) cin >> a[i].x >> a[i].y >> a[i].z;

    //初始化并查集
    for (int i = 1; i <= n; i++) fa[i] = i;

    int ret = kk();
    if (ret == INF) cout << "orz" << endl;
    else cout << ret << endl;
    
    return 0;
}
P1194 买礼物 - 洛谷

题⽬转化:

  • 如果把每⼀个零⻝看成⼀个节点,优惠看成⼀条边,就变成在图中找最⼩⽣成树的问题。
  • 因此,跑⼀遍kk算法即可。
    注意:
  1. 存边的时候,没有必要存重复的,并且权值过⼤的也不需要存;
  2. 最终提取结果的时候,虽然有可能构造不出来⼀棵最⼩⽣成树,但是要在已有的构造情况下处理结果
#include <bits/stdc++.h>
using namespace std;

const int N = 500 * 500 + 10;

int a, n;

int pos;
struct node
{
    int x, y, z;
}e[N];

int fa[N];

int find (int x)
{
    return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}

int cnt, ret;

bool cmp(node& a, node& b)
{
    return a.z < b.z;
}

void kk()
{
    sort(e+1, e+1+pos, cmp);
    
    for (int i = 1; i <= pos; i++)
    {
        int x = e[i].x, y = e[i].y, z = e[i].z;

        int fx = find(x), fy = find(y);
        if (fx != fy)
        {
            cnt++;
            ret += z;
            fa[fx] = fy;
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> a >> n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
        {
            int k; cin >> k;
            if (i >= j || k > a || k == 0) continue;
            pos++;
            e[pos].x = i; e[pos].y = j; e[pos].z = k;
        }

    for (int i = 1; i <= n; i++) fa[i] = i;
    kk();
    cout << ret + (n - cnt) * a << endl;
    
    return 0;
}
P2330 [SCOI2005] 繁忙的都市 - 洛谷

定理:最⼩⽣成树就是瓶颈⽣成树。
在kk算法中,维护边权的最⼤值即可

#include <bits/stdc++.h>
using namespace std;

const int N = 310, M = 8010;

int n, m;
struct node
{
    int x, y, z;
}e[M];

int fa[N];

int find (int x)
{
    return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}

int ret; //最大边的权值

bool cmp(node& x, node& y)
{
    return x.z < y.z;
}

void kk()
{
    for (int i = 1; i <= n; i++) fa[i] = i;
    
    sort(e+1, e+1+m, cmp);

    for (int i = 1; i <= m; i++)
    {
        int x = e[i].x, y = e[i].y, z = e[i].z;

        int fx = find(x), fy = find(y);
        if (fx != fy)
        {
            ret = max(ret, z);
            fa[fx] = fy;
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m;
    for (int i = 1; i <= m; i++) cin >> e[i].x >> e[i].y >> e[i].z;

    cout << n - 1 << " ";
    
    kk();
    cout << ret << endl;
    
    return 0;
}
P2573 [SCOI2012] 滑雪 - 洛谷

第⼀问:从起点开始,做⼀次dfs/bfs就可以扫描到所有的点。
第⼆问:因为有回溯的效果,相当于就是选择⼀些边,把所有的点都连接起来。但是需要注意:

  • 由于这些边是有⽅向的,我们只要保证能从1位置出发,访问到所有的点即可。与最⼩⽣成树还是有差异的。
  • 为了能保证选出来的边能够从1访问所有点,应该优先考虑去往更⾼位置的边,这样才能向下⾛到更低的位置
#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 1e5 + 10, M = 2e6 + 10;

int n, m;
int h[N];

vector<PII> edges[N];

int fa[N];

int find(int x)
{
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

LL cnt, ret;
bool st[N];

int pos;
struct node
{
  int x, y, z;  
}e[M];

void dfs(int u)
{
    cnt++;
    st[u] = true;

    for (auto& p : edges[u])
    {
        int v = p.first, k = p.second;
        pos++;
        e[pos].x = u; e[pos].y = v; e[pos].z = k;

        if (!st[v]) dfs(v);
    }
}

bool cmp(node& a, node& b)
{
    int y1 = a.y, z1 = a.z, y2 = b.y, z2 = b.z;
    if (h[y1] != h[y2]) return h[y1] > h[y2];
    else return z1 < z2;
}

void kk()
{
    for (int i = 1; i <= n; i++) fa[i] = i;
    
    sort (e+1, e+1+pos, cmp);

    for (int i = 1; i <= pos; i++)
    {
        int x = e[i].x, y = e[i].y, z = e[i].z;

        int fx = find(x), fy = find(y);
        if (fx != fy)
        {
            ret += z;
            fa[fx] = fy;
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> h[i];
    for (int i = 1; i <= m; i++)
    {
        int x, y, z; cin >> x >> y >> z;    
        if (h[x] >= h[y]) edges[x].push_back({y, z});
        if (h[x] <= h[y]) edges[y].push_back({x, z});
    }

    dfs(1);
    cout << cnt << " ";

    kk();
    cout << ret << endl;
    
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2337912.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第十四节:实战场景-何实现全局状态管理?

React.createElement调用示例 Babel插件对JSX的转换逻辑 React 全局状态管理实战与 JSX 转换原理深度解析 一、React 全局状态管理实现方案 1. Context API useReducer 方案&#xff08;轻量级首选&#xff09; // 创建全局 Context 对象 const GlobalContext createConte…

数据驱动、精准协同:高端装备制造业三位一体生产管控体系构建

开篇引入 鉴于集团全面推行生产运营体建设以及对二级单位生产过程管控力度逐步加强&#xff0c;某高端装备制造企业生产部长王总正在开展新的一年企业生产管控规划工作&#xff0c;为了能够更好地进行体系规划与建设应用&#xff0c;特邀请智能制造专家小智来进行讨论交流。 王…

航电系统之通信技术篇

航电系统&#xff08;航空电子系统&#xff09;的通信技术是现代航空器的核心技术之一&#xff0c;其核心目标是实现飞行器内部各系统之间以及飞行器与外部设备&#xff08;如地面控制中心、其他飞行器等&#xff09;之间高效、可靠的信息交互。随着航空技术的不断发展&#xf…

发现“横”字手写有难度,对比两个“横”字

我发现手写体“横”字“好看”程度&#xff0c;难以比得上印刷体&#xff1a; 两个从方正简体启体来的“横”字&#xff1a; 哪个更好看&#xff1f;我是倾向于左边一点。 <div style"transform: rotate(180deg); display: inline-block;"> 左边是我从方正简…

深度学习3.1 线性回归

3.1.1 线性回归的基本概念 损失函数 梯度下降 3.1.2 向量化加速 %matplotlib inline import math import time import numpy as np import torch from d2l import torch as d2ln 1000000 #本机为了差距明显&#xff0c;选择数据较大&#xff0c;运行时间较长&#xff0c;可选…

番外篇 | SEAM-YOLO:引入SEAM系列注意力机制,提升遮挡小目标的检测性能

前言:Hello大家好,我是小哥谈。SEAM(Squeeze-and-Excitation Attention Module)系列注意力机制是一种高效的特征增强方法,特别适合处理遮挡和小目标检测问题。该机制通过建模通道间关系来自适应地重新校准通道特征响应。在遮挡小目标检测中的应用优势包括:1)通道注意力增强…

SpringBoot ApplicationEvent:事件发布与监听机制

文章目录 引言一、事件机制的基本概念二、创建自定义事件2.1 定义事件类2.2 发布事件2.3 简化的事件发布 三、创建事件监听器3.1 使用EventListener注解3.2 实现ApplicationListener接口3.3 监听非ApplicationEvent类型的事件 四、事件监听的高级特性4.1 条件事件监听4.2 异步事…

广东2024信息安全管理与评估一阶段答案截图

2023-2024 学年广东省职业院校技能大赛 高等职业教育组 信息安全管理与评估 赛题一 模块一 网络平台搭建与设备安全防护 一、 比赛时间 本阶段比赛时间为 180 分钟。 二、 赛项信息 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 第一…

安卓手机如何改ip地址教程

对于安卓手机用户而言&#xff0c;ip修改用在电商、跨境电商、游戏搬砖、社交软件这些需要开多个账号的项目。因为多个设备或账号又不能在同一ip网络下&#xff0c;所以修改手机的IP地址防检测成为一个必要的操作。以下是在安卓手机上更改IP地址的多种方法及详细步骤&#xff0…

(51单片机)LCD显示数据存储(DS1302时钟模块教学)(LCD1602教程)(独立按键教程)(延时函数教程)(I2C总线认识)(AT24C02认识)

目录 演示视频&#xff1a; 源代码 main.c LCD1602.c LCD1602.h AT24C02.c AT24C02.h Key.c Key.h I2C.c I2C.h Delay.c Delay.h 代码解析与教程&#xff1a; Dealy模块 LCD1602模块 Key模块 I2C总线模块 AT24C02模块 /E2PROM模块 main模块 演示视频&#xff1a; &…

STL简介 + string【上】

一 . STL简介 1.1 什么是STL STL&#xff08;standard template libaray - 标准模板库) : 是C标准库的重要组成部分 &#xff0c; 不仅是一个可复用的组件库 &#xff0c; 而且是一个包罗 数据结构 与 算法 的软件框架 。 注意 &#xff1a; 是标准库的一部分 &#xff…

【Bluedroid】A2DP Sink播放流程源码分析(二)

接上一篇继续分析:【Bluedroid】A2DP Sink播放流程源码分析(一)_安卓a2dp sink播放流程-CSDN博客 AVDTP接收端(Sink)流事件处理 bta_av_sink_data_cback 是 Bluedroid 中 A2DP Sink 角色的 AVDTP 数据回调函数,负责处理接收端的音频数据事件,将底层接收到的音频数据传递…

redis利用备忘录

fofa: icon_hash"864611937" 防护&#xff1a; redis的安全设置&#xff1a;设置完毕&#xff0c;需要重加载配置文件启动redis 1.绑定内网ip地址进行访问 2. requirepass设置redis密码 3.保护模式开启protected-mode开启&#xff08;默认开启&#xff09; 4.最好把…

SAP系统中MD01与MD02区别

知识点普及&#xff0d;MD01与MD02区别 1、从日常业务中&#xff0c;我们都容易知道MD01是运行全部物料&#xff0c;MD02是运行单个物料 2、在做配置测试中&#xff0c;也出现过MD02可以跑出物料&#xff0c;但是MD01跑不出的情况。 3、MD01与MD02的差异: 3.1、只要在物料主数…

ubuntu学习day3

3 编译与调试 3.1 gcc/g编译器 当我们进行编译的时候&#xff0c;要使用一系列的工具&#xff0c;我们称之为工具链。SDK就是编译工具链的简写&#xff0c;我们所使用的是gcc系列编译工具链。使用-v参数来查看gcc的版本&#xff0c;从而确定某些语法特性是否可用&#xff0c;…

6.8.最小生成树

一.复习&#xff1a; 1.生成树&#xff1a; 对于一个连通的无向图&#xff0c;假设图中有n个顶点&#xff0c;如果能找到一个符合以下要求的子图&#xff1a; 子图中包含图中所有的顶点&#xff0c;同时各个顶点保持连通&#xff0c; 而且子图的边的数量只有n-1条&#xff0…

QT中栅格模式探索

1、Qt中选择了栅格模式&#xff0c;如下图所示&#xff1a; 2、在进行整个大的UI界面布局时&#xff0c;需了解每个控件所需要选择的属性sizePolicy。 sizePolicy包含如下几种选择&#xff1a; 3、举个例子&#xff1a;此时整个UI界面&#xff0c;我采用了栅格模式&#xf…

C++入门基础:命名空间,缺省参数,函数重载,输入输出

命名空间&#xff1a; C语言是基于C语言的&#xff0c;融入了面向对象编程思想&#xff0c;有了很多有用的库&#xff0c;所以接下来我们将学习C如何优化C语言的不足的。 在C/C语言实践中&#xff0c;在全局作用域中变量&#xff0c;函数&#xff0c;类会有很多&#xff0c;这…

tomcat 的安装与启动

文章目录 tomcat 服务器安装启动本地Tomcat服务器 tomcat 服务器安装 https://tomcat.apache.org/下载 Tomcat 10.0.X 启动本地Tomcat服务器 进入 Tomcat 的 bin

算法-堆+单调栈

堆 首先堆在我们的Java中我们的是一个优先队列类 PriorityQueue 然后我们要弄最大堆和最小堆 最大堆&#xff1a; PriorityQueue<Integer> pq new PriorityQueue<Integer>((a, b) -> b - a); 最小堆&#xff1a; PriorityQueue<Integer> pq new P…