最小生成树|二分图

news2024/11/15 15:31:04

最小生成树跟边的正负没有任何关系。

最小生成树

朴素Prime

该算法和Dijkstr算法很像。

  1. 先把所有距离初始化为正无穷

  1. 进行n次迭代

  1. 找到不在集合(集合指当前的生成树)当中的点,s数组表示当前已经在连通块(生成树)中的所有点。

  1. 找到集合外距离最近的点赋给t,用t更新其它点到集合的距离

  1. s加入到t当中去。

首先让所有点到集合的距离都是正无穷,然后找到一个集合外到集合距离最近的点,这四个点距离都是正无穷,我们则可以随便挑一个点

这里我们挑1号点,之后找到1号点到集合的最短距离,若该点没有边连到集合,则该点到集合的距离还是正无穷。

若找到了1号点到集合的最短距离,则用1号点去更新所有其它点到集合的距离

之后把1号点(绿点)加到集合当中去。

第二次迭代,从剩下的点当中选一个到集合距离最近的点,这里2号点最近,选中2号点之后,用2号点更新其它点到集合的距离(这个例子中更新前后结果不变,因为3到1和3到2的距离都是2,而且2和4没有连接),更新完之后,把2号点加到集合里面去。

重复进行上述操作 ,在3和4中找距离最近的点,这里3最近,用3更新4到集合的距离时,3到4的距离是4,比1到4的距离3要大,所以这里不更新。我们的目的是将距离更新的更短,距离如果变长,我们就不更新。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N= 510,INF=0x3f3f3f3f;
int n, m;
int g[N][N];//某俩点间的距离
int dist[N];//某点到集合的距离
bool st[N];
int prim()
{
    memset(dist, 0x3f, sizeof dist);
    int res = 0;//存储最小生成树所有边的长度之和
    for (int i = 0; i < n; ++i)
    {
        int t = -1;
        for (int j = 1; j <= n; j++)//找到集合外所有距离最小的点
        {
            if (!st[j] && (t == -1 || dist[t] > dist[j]))//t=-1表示当前还没有找到距离最小的点
                //如果t代表的点到集合的距离大于j代表的点到集合的距离,也表示当前没找到
                t = j;
        }
            if (i && dist[t] == INF)//说明当前距离最近的点到集合的距离是正无穷,即所有点没有连通
            {
                return INF;
            }
            if (i) res += dist[t];//dist[t]表示当前点和另一个点(该点已与集合连好)的距离
            for (int j = 1; j <=n; ++j)
            {
                dist[j] = min(dist[j], g[t][j]);//更新其它点
            }
        
            st[t] = true;//修改状态
        
    }
    return res;
}
int main()
{
    scanf("%d %d", &n, &m);
    memset(g, 0x3f, sizeof g);
    while (m--)//读入所有边
    {
        int a, b, c;
        scanf("%d %d %d", &a, &b, &c);
        g[a][b] = g[b][a]=min(g[a][b],c);//由于不用考虑边的正负,a到b的距离和b到a的距离都一样,因为可能有重边,我们取一个最小值
    }
    int t = prim();
    if (t == INF)//不存在生成树
        puts("impossible");
    else//存在生成树
        printf("%d", t);
    return 0;
}

克鲁斯卡尔算法

  1. 先将所有边按权重从小到大进行排序

  1. 枚举每条边a,b权重为c

  1. 如果当前a,b不连通,就把a,b这条边加到集合里面来

第二步其实是前面所学并查集的应用

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 200010;
int n, m;
int p[N];

struct Edge
{
    int a, b, w;//a,b是边的俩条点,w是权重
    bool operator<(const Edge& W) const//重载小于号方便排序
    {
        return w < W.w;
    }
}edges[N];
int find(int x)
{
    if (p[x] != x)//如果x不是祖宗节点
        p[x] = find(p[x]);
    return p[x];
}
int main()
{
    scanf("%d %d", &n, &m);
    for (int i = 0; i < m; ++i)
    {
        int a, b, w;
        scanf("%d %d %d", &a, &b, &w);//把所有的边读进来
        edges[i] = { a,b,w };
    }
    sort(edges, edges + m);//把所有的边排序
    for (int i = 1; i <=n; ++i)//初始化并查集
        p[i] = i;
    int res = 0, cnt = 0;//res存的是最小生成树当中所有树边的权重之和,cnt是当前加了多少条边
    for (int i = 0; i < m; ++i)//从小到大枚举所有边
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;
        a = find(a), b = find(b);//a=a的祖宗节点,b=b的祖宗节点,find函数是并查集的find函数
        if (a != b)//如果俩个祖宗节点不连通
        {
            p[a] = b;//这条边加进来之后,要把俩个点各自所在的集合进行合并
            res += w;
            cnt++;
        }
    }
    if (cnt < n - 1)//如果连的边数<n-1,说明这个树是不连通的
        puts("impossible");
    else
        printf("%d\n",res);
    return 0;
}

二分图

二分图定义:所有点划分到俩边区,使所有的边都是在集合之间的,集合内部没有边。如果一个图中有奇数环,则一定不是二分图。把图分为俩个区域,一个是左边,另一个是右边。

染色法

如果一个图中不含有奇数环则一定是二分图。

  1. 从前往后遍历每一个点

  1. 如果某个点还没分好组,我们就分到左边去

  1. 分完该点之后,遍历这个点和所有的邻点(和这个点连通的所有点),若该点属于左边,则它的所有相邻点属于右边,若该点属于右边,则它的所有相邻点属于左边,下图中第一个点属于左边,1代表左边,2代表右边,按照这种方式,将所有点可表示出来,其实就是在染色。

  1. 染色的时候要注意:一条边的俩端点的颜色,一定不同,即这俩个点不能属于同一个集合。通过这种方式将图中的所有点进行染色。由于图中不存在奇数环,所以染色过程中不会出现矛盾。

只要一个图可以用染色法染完,而且不出现矛盾,它就是一个二分图。

这里采用深度优先遍历进行染色。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1010, M = 2010;//由于是无向图,每条无向边要存俩条有向边,即俩个点之间有俩条线,所以M是N的2倍
int n, m;
int h[N], e[M], ne[M], idx;
int color[N];
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool dfs(int u,int c)
{
    color[u] = c;//记录一下当前点颜色是C
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];//存储当前点的编号
        if (!color[j])//如果当前点没有颜色
        {
            if (!dfs(j,3-c)) //如果当前是颜色1,就染成2,如果是颜色2,就染成1,这里写成3-c
                return false;//如果染色不成功就返回false,成功返回true
        }
        else if (color[j]==c)//如果这俩个相等,代表一条边的俩端点颜色一样,也有矛盾
        {
            return false;
        }
    }
    return true;
}
int main()
{
    scanf("%d %d", &n, &m);
    memset(h, -1, sizeof h);
    while (m--)
    {
        int a, b;
        scanf("%d %d", &a, &b);
        add(a, b);
        add(b, a);//由于是无向边,所以这里加俩条边,一条a到b,一条b到a

    }
    //开始染色
    bool flag = true;//标志位,表示染色成功或失败
    for(int i=1;i<=n;++i)
        if (!color[i])
        {
            if (!dfs(i, 1))//如果有矛盾dfs返回fasle,这里传1是想把有矛盾的地方染成第一种颜色,
            {
                flag = false;
                break;
            }
        }
    if (flag)
        puts("Yes");
    else
        puts("No");//有矛盾发生
    return 0;

}

匈牙利算法

假设有这样的一个二分图

匈牙利算法可以在一个比较块的时间内,算出左边和右边最大的匹配成功(不存在俩条边共用一个点)数是多少。

1.如果a和b点已经匹配好了,c点要和b匹配,此时c点找一下有没有其它能和a匹配的点,如果有,就让a和其它点去匹配,然后c自己和b匹配。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 510, M = 100010;
int n1, n2, m;
int h[N], e[M], ne[M], idx;
int match[N];//右边对应的点
bool st[N];//右边的某点是否匹配成功
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool find(int x)
{
    for (int i = h[x]; i != -1; i= ne[i])//遍历左边的点
    {
        int j = e[i];//当前左边点的编号
        if (!st[j])//如果当前右边的点还未匹配
        {
            st[j] = true;//进行匹配
            if (match[j] == 0||find(match[j]))//0表示还没匹配,或者说已经匹配到了左边的某点,但左边的这个点可以去匹配别的点
            {
                match[j] = x;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    scanf("%d %d %d", &n1, &n2, &m);
    memset(h, -1, sizeof h);
    while (m--)
    {
        int a, b;
        scanf("%d %d", &a, &b);
        add(a, b);
    }
    int res = 0;//当前匹配的数量
    for (int i = 1; i <= n1; ++i)
    {
        memset(st, false, sizeof st);
        if (find(i)) res++;//如果左边右边匹配成功,直接++
    }
    printf("%d\n", res);
    return 0;
}

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

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

相关文章

vue-simple-uploader在VUE3中分片上传大文件

vue-simple-uploader是一个优秀的大文件分片上传组件&#xff0c;在vue2里面工作一切正常&#xff0c;但是在vue3里面却发现了一些问题&#xff1a; &#xff08;1&#xff09;在element-plus的el-dialog里面渲染失败&#xff1b; &#xff08;2&#xff09;上传进度条不能正…

由浅入深详解四种分布式锁

在多线程环境下&#xff0c;为了保证数据的线程安全&#xff0c;锁保证同一时刻&#xff0c;只有一个可以访问和更新共享数据。在单机系统我们可以使用synchronized锁或者Lock锁保证线程安全。synchronized锁是Java提供的一种内置锁&#xff0c;在单个JVM进程中提供线程之间的锁…

信息系统项目管理师,第四版应该如何应对

一、 改版情况。 2023年3月&#xff0c;新版教材起售。 2023年4月4日&#xff0c;官网宣布本次考试第三版第四版兼顾使用。 以历年的考试时间来看&#xff0c;一般这次考试是在 5月27日、28日附近。 接下来只有一个月左右的时间了。给大家聊聊面对现在这个情况如何备考。 …

考研计算机组成原理总结(7)

一.虚拟存储器 1.基本知识 主存和辅存共同构成了虚拟存储器&#xff0c;二者在硬件和系统软件的共同管理下工作。对于应用程序员而言&#xff0c;虚拟存储器是透明的。虚拟存储器具有主存的速度和辅存的容量。 2.基本概念 虚拟存储器将主存或辅存的地址空间统一编址&#x…

如何提高图片清晰度?三种方法来帮你!

如何提高图片清晰度&#xff1f;图片在上传到网络后会被压缩&#xff0c;导致图片变得模糊。今天&#xff0c;我将分享三种方法&#xff0c;帮助您提高图片的清晰度。 方法一&#xff1a;使用记灵在线工具 工具地址&#xff1a;记灵在线工具 - 更专注于发现工具的实用性 该工…

[Linux 命令] ls 显示目录内容列表

ls 显示目录内容列表 思维导图 补充说明 ls命令 就是list的缩写&#xff0c;用来显示目标列表&#xff0c;在Linux中是使用率较高的命令。ls命令的输出信息可以进行彩色加亮显示&#xff0c;以分区不同类型的文件。 语法 ls [选项] [文件名...][-1abcdfgiklmnopqrstuxABCD…

Node.js下载安装与简单使用

一、下载Node.js 打开链接&#xff1a;Node.js 的官网首页&#xff08;https://nodejs.org/en/&#xff09; 选择左边的 LTS 版本和 Current 版本的不同 1.LTS 为长期稳定版&#xff0c;对于追求稳定性的企业级项目来说&#xff0c;推荐安装 LTS 版本的 Node.js。 2.Current …

【系统集成项目管理工程师】项目质量管理

&#x1f4a5;十大知识领域&#xff1a;项目质量管理 项目质量管理包括以下 3 个过程: 规划质量管理实施质量保证质量控制 一、规划质量管理 规划质量管理是识别项目及其可交付成果的质量要求和标准&#xff0c;并准备对策确保符合质量要求的过程 这部分重点主要是工具与技术 1…

C语言从入门到精通第8天(分支结构if、else、switch的使用)

分支结构if、else、switch的使用 if语句if...else语句if...else嵌套if...else if...else语句switch语句 if语句 语法&#xff1a; if(表达式){ 语句&#xff1b; } 如果表达式为真&#xff0c;则执行{}里面的语句。如果为假&#xff0c;则不执行。示例代码&#xff1a; int m…

春秋云镜:CVE-2022-25488(SQL报错注入)

目录 一、题目 二、sqlmap梭哈查flag 一、题目 介绍&#xff1a; Atom CMS v2.0存在sql注入漏洞在/admin/ajax/avatar.php页面 进入题目; 发现是Not Found页面 一开始以为打开的问题&#xff1a; 一想 我们要访问的是/admin/ajax/avatar.php 访问后是空白页面&#xff1a…

进程树pstree介绍

进程树&#xff08;pstree&#xff09;是一个Linux/Unix命令&#xff0c;用于显示系统中所有进程的层次结构。它可以将进程展示为树形结构&#xff0c;其中每个进程都是一个节点&#xff0c;而每个节点下面的子节点是该进程的子进程。pstree可以按照进程的父子关系显示进程&…

[架构之路-172]-《软考-系统分析师》-5-数据库系统-5- 数据库设计与建模(逻辑设计-实体关系图ER图-关系图、物理设计)

目录 5 . 5 数据库设计与建模 5.5.1数据库设计阶段 1 . 规划&#xff1a;为什么做&#xff1f;能不能做&#xff1f; 2 . 需求分析&#xff1a;做成什么样子&#xff1f; 3 . 概念设计&#xff1a;怎么做 - 概念 &#xff08;用户&#xff09; 4 . 逻辑设计&#xff1a;怎…

p66 内网安全-域横向批量atschtasksimpacket

数据来源 本文仅用于信息安全的学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若观众因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与本人无关。 基本概念 DMZ区域&#xff1a;称为“隔离区”&#xff0c;也称‘’非军事化区/停火区…

C语言入门篇——数据篇

目录 1、变量与常量 1.1变量 1.2常量 1.2.1#define 定义的标识符常量 1.2.2枚举常量 2、数据类型关键字 3、整数 4、浮点数 5、基本数据类型 5.1、int型数据 5.2、char型数据 5.3、_Bool类型 5.4、float、double和long double 5.5、复数和虚数类型 6、总结 1、变…

Preempt-RT实时系统下IGH主站安装

文章目录 1. 安装环境2. 确定网卡类型3. 下载IGH安装包4. 配置安装5. 启动测试6. 扫描从站7. 设置环境变量 1. 安装环境 ubuntu18.04内核版本&#xff1a;4.19.72-rt25 2. 确定网卡类型 查看网卡驱动 lspci -v可以看到我的网卡驱动主要有e1000e和igb两种类型&#xff0c;其…

localhost与本机IP IPtables匹配顺序

localhost&host IPtables iptables链匹配顺序 ping localhost 当前实验网络为IPV4模式通过tcpdump抓包&#xff0c;我们可以发现ping localhost的流量最终发送到lo网卡了通过iptables pkts数据计数我们可知&#xff0c;ping localhost的iptables过滤流程为&#xff1a;应…

npm install 卡住 不动弹

npm install时如果卡住&#xff0c;不动弹&#xff0c;可以试试以下几种方式。 设置注册中心&#xff0c;使用淘宝注册中心。 这种主要解决因为网络问题引起的下载失败&#xff0c;npm config set registry https://registry.npmmirror.com&#xff0c;设置完后使用npm config…

阻止or关闭Win10自动更新

阻止or关闭Win10自动更新 https://baijiahao.baidu.com/s?id1732432888882246429&wfrspider&forpc 一、禁用Windows Update服务 1、同时按下键盘 Win R&#xff0c;打开运行对话框&#xff0c;然后输入命令 services.msc &#xff0c;点击下方的“确定”打开服务。…

轻松掌握k8s的kubectl使用命令行操作Service知识点02

1、Service将同类型一组应用统一IP访问 将一组 Pods 网络服务的抽象方法。统一Ip后&#xff0c;默认就实现了负载均衡。 1、只在Pod内部任意机器访问的ClusterIp类型 在命令行操作生成一个ClusterIp地址。这种ClusterIp只能在Pod内部访问。 生成了ClusterIp之后&#xff0…

Git的安装和学习使用(一)

本篇文章旨在分享本人在学习Git时的随笔记&#x1f929; 文章目录 一、Git 快速入门1.1 Git 概述1.2 SCM概述1.3 Git 安装1.3.1 软件下载1.3.2 软件安装1.3.3 软件测试 二、Git 基础使用2.1 Git 概念2.1.1 版本控制2.1.2 分布式2.1.3 系统2.1.4 区域 2.2 Git 基础指令2.2.1 Lin…