最小生成树刷题笔记

server/2024/9/22 13:53:39/

算法基础:

最小生成树是所有节点的最小连通子图!!!!

首先是prim算法三部曲:

(1)找到距离最小生成树最近的节点。

(2)将距离最小生成树最近的节点加入到最小生成树中。

(3)更新非最小生成树节点到最小生成树的距离。

实现步骤:

首先我们利用一个for循环遍历n - 1遍,因为我们从第1个节点开始将其加入到生成树之中后知道添加到还剩两个节点时,我们可以发现当我们添加玩倒数第二个节点后,最后一个节点的mindist数值在处理倒数第二个节点的第三部更新过程中已经得到了,这个距离不是倒数第二个节点到它的距离grid[cur][j]就是之前已经得到的它与某个节点之间的距离mindist[j]。

(1)寻找最近节点:

判断最近节点的三个条件:1.未在生成树中。  2.距离生成树距离最短     3.选取最短距离我们先随便选出一个节点然后再与其他节点比较

if(!isvisited[j] && (cur = -1 || mindist[cur] > mindist[j])              Cur = j;

这里:Mindist[cur] < mindist[j]包含了两层含义:首先我们要明确我们要找离生成树距离最短的点,mindist数组中存的只就是节点到生成树的最小距离,如果后续的节点的mindist的值要小于前面得出的mindist值,那么就将当前节点的标记j赋值给cur。

这里我们每遍历一层就会将cur置为-1以便我们可以最快选取一个随机节点并遍历后面的节点与之比较。而第二层的mindist[j]的值已经在第一层的第三步更新操作中得到。

(2)将最近节点加入生成树:

isintree[cur] = true;

(3)更新非生成树节点到生成树距离:

利用一个for循环遍历所以非生成树节点,并更新起距离mindist[j] < grid[cur][j] ? Mindist[j] = mindist[j] : mindist[j] = grid[cur][j]

这列的更新操作有两层意思:第一是遍历的外部节点需要与生成树有连接,也就是grid[cur][j] < mindist[j] == INT_MAX,第二是当前节点已经跟生成树有连接但还没选入进入生成树(预备党员哈哈)因为之前节点的更新扩散操作使得与之前节点有连接的j节点有了mindist的数值,所以我们拿grid[cur][j]与之前得到的最近距离mindist作比较保留更小的那个值赋值给mindist[j]

经过上面的分析,我认为prim算法的关键一定是要明确mindist数组的含义:是当前节点到最小生成树的最小距离。因为我们的第一步选点用到mindist数组来根据各个点到生成树(第一步时就是到源点的距离)的最小距离来选取最近节点,我们的第三步更新也是由于新加入的点导致未加入节点到生成树的距离改变,通过grid[cur][j] 与mindist[j]来判断是否需要更改mindist的值。

#include<iostream>
#include<vector>
using namespace std;
int main() {
    int v, e;
    int x, y, k;
    cin >> v >> e;
    // 填一个默认最大值,题目描述val最大为10000
    vector<vector<int>> grid(v + 1, vector<int>(v + 1, 10001));
    while (e--) {
        cin >> x >> y >> k;
        // 因为是双向图,所以两个方向都要填上
        grid[x][y] = k;
        grid[y][x] = k;

    }
    // 所有节点到最小生成树的最小距离
    vector<int> minDist(v + 1, 10001);

    // 这个节点是否在树里
    vector<bool> isInTree(v + 1, false);

    // 我们只需要循环 n-1次,建立 n - 1条边,就可以把n个节点的图连在一起
    for (int i = 1; i < v; i++) {

        // 1、prim三部曲,第一步:选距离生成树最近节点
        int cur = -1; // 选中哪个节点 加入最小生成树
        for (int j = 1; j <= v; j++) { // 1 - v,顶点编号,这里下标从1开始
            //  选取最小生成树节点的条件:
            //  (1)不在最小生成树里
            //  (2)距离最小生成树最近的节点
            //  (3)只要不在最小生成树里,先默认选一个节点 ,在比较 哪一个是最小的
            //  理解条件3 很重要,才能理解这段代码:(cur == -1 || minDist[j] < minDist[cur])
            if (!isInTree[j] && (cur == -1 || minDist[j] < minDist[cur])) {
                cur = j;
            }
        }
        // 2、prim三部曲,第二步:最近节点(cur)加入生成树
        isInTree[cur] = true;

        // 3、prim三部曲,第三步:更新非生成树节点到生成树的距离(即更新minDist数组)
        // cur节点加入之后, 最小生成树加入了新的节点,那么所有节点到 最小生成树的距离(即minDist数组)需要更新一下
        // 由于cur节点是新加入到最小生成树,那么只需要关心与 cur 相连的 非生成树节点 的距离 是否比 原来 非生成树节点到生成树节点的距离更小了呢
        for (int j = 1; j <= v; j++) {
            // 更新的条件:
            // (1)节点是 非生成树里的节点
            // (2)与cur相连的某节点的权值 比 该某节点距离最小生成树的距离小
            // 很多录友看到自己 就想不明白什么意思,其实就是 cur 是新加入 最小生成树的节点,那么 所有非生成树的节点距离生成树节点的最近距离 由于 cur的新加入,需要更新一下数据了
            if (!isInTree[j] && grid[cur][j] < minDist[j]) {
                minDist[j] = grid[cur][j];
            }
        }
    }
    // 统计结果
    int result = 0;
    for (int i = 2; i <= v; i++) { // 不计第一个顶点,因为统计的是边的权值,v个节点有 v-1条边
        result += minDist[i];
    }
    cout << result << endl;

}

leetcode - 1584:连接所有点的最小费用

由于这道题给出的是点集,我们可以想到使用prim来处理点集的最小生成树问题,这道题不好使用kruskal来解决因为还要求边那就需要我们去求各个点的组合会复杂很多。

就是利用题目中的公式建立邻接矩阵在套用上面的模板即可:

class Solution {
public:
    int minCostConnectPoints(vector<vector<int>>& points) {
        int n = points.size();
        vector<vector<int>> grid(n, vector<int>(n, 0));
        // 计算任意两点之间的曼哈顿距离并填充到grid矩阵中
        for(int i = 0; i < n; i++){
            for(int j = i + 1; j < n; j++){
                int x = abs(points[i][0] - points[j][0]);
                int y = abs(points[i][1] - points[j][1]);
                grid[i][j] = grid[j][i] = x + y;
            }
        }
        vector<bool> isvisited(n, false);
        vector<int> mindist(n, INT_MAX);
        mindist[0] = 0; // 从第一个点开始
        int res = 0;
        for(int i = 0; i < n - 1; i++){
            int cur = -1;
            // 选择当前距离生成树最近的节点
            for(int j = 0; j < n; j++){
                if(!isvisited[j] && (cur == -1 || mindist[j] < mindist[cur])){
                    cur = j;
                }
            }
            isvisited[cur] = true;
            // 更新其他节点到生成树的距离
            for(int j = 0; j < n; j++){
                if(!isvisited[j] && mindist[j] > grid[cur][j]){
                    mindist[j] = grid[cur][j];
                }
            }
        }
        for(int i = 1; i < n; i++){
            res += mindist[i];
        }
        return res;
    }


http://www.ppmy.cn/server/40359.html

相关文章

离线修复.dll,Microsoft Visual C++

在安装mysql时遇到下面的问题&#xff0c;如果是有网络的情况下微软管网下载安装就行了&#xff0c;用的服务器不允许连接互联网。 后面经过寻找&#xff0c;找到了一个修复工具&#xff0c;可一次修复所有的问题&#xff0c;特别好用分享给宝子们。 下载链接&#xff1a;http…

企业级复杂前中台项目响应式处理方案

目录 01: 前言 02: 响应式下navigtionBar实现方案分析 数据 视图 小结 03: 抽离公用逻辑&#xff0c;封装系列动作 04: PC端navigationBar私有逻辑处理 05: 分析 navigationBar 闪烁问题 06: 处理 navigationBar 闪烁问题 07: category数据缓存&#xff0c;覆盖…

机器学习案例:加州房产价格(二)

参考链接&#xff1a;https://hands1ml.apachecn.org/2/ 设计好系统后&#xff0c;要开始在工作区编写代码来解决问题了。 下载数据 首先我们需要先得到数据集。 一般情况下&#xff0c;数据是存储于关系型数据库&#xff08;或其它常见数据库&#xff09;中的多个表、文档、…

无线通信模块通过TCP/IP协议实现与PC端的数据传输

在当今的信息时代&#xff0c;无线通信技术的发展日新月异&#xff0c;为我们的工作和生活带来了极大的便利。其中&#xff0c;无线通信模块通过TCP/IP协议向PC端传送数据已经成为了一种常见的通信方式。本文将详细介绍这一过程的主要步骤和涉及的关键技术&#xff0c;并以WIFI…

Oracle导入数据中文乱码问题处理,修改客户端字符编码跟数据库的一致

前提&#xff1a;SQL文件打开其中中文字符是正常显示&#xff0c;保证导出文件中文字符正常。 通过sqlplus命令导入SQL文件出现乱码&#xff0c;这是因为客户端跟数据库的字符集不一致导致出现乱码问题。 要SQL导入的中文正常&#xff0c;要确保执行导入命令的客户端字符编码…

理解并实现区块链智能合约

理解并实现区块链智能合约是一个涉及多个技术层面的过程。智能合约是自动执行、管理区块链上交易或协议的程序。它们在满足预设条件时自动执行合约条款&#xff0c;从而减少了中间人的需要&#xff0c;并提高了透明度和效率。下面是智能合约的基本概念和实现步骤&#xff1a; …

MySQL性能优化(提升数据库性能的措施)

万物皆有裂痕&#xff0c;那是光照进来的地方。大家好&#xff0c;今天给大家分享一下关于MySQL性能优化&#xff0c;在处理大型数据集和高负载情况下&#xff0c;MySQL数据库的性能优化是至关重要的。通过合理的调优策略&#xff0c;可以有效提高数据库的响应速度和稳定性。本…

Nginx最详细入门教程

Nginx 一、Nginx入门介绍 1.1、Nginx简介 1.2、Nginx和Apache 二、安装配置Nginx 2.1、安装配置 2.2、配置文件常规优化 2.3、虚拟主机 三、LNMP架构及应用部署 3.1、安装MySQL数据库 3.2、安装PHP 3.3、配置Nginx支持PHP环境 3.4、在LNMP平台部署Web应…