算法竞赛进阶指南学习--0x20

news2024/11/25 18:40:22

 注意:我们统一用邻接矩阵和邻接表来存储树和图

int h[N], e[N], ne[N], w[N], idx;
int g[N][N];
void add(int a, int b, int c) //a->b 边权为c
{
    e[idx] = b;
    w[idx] = c
    ne[idx] = h[a];
    h[a] = idx ++;
}

void add(int a,int b, int c)
{
    g[a][b] = c;
}

0x21树和图的遍历

树与图的dfs遍历,dfs序,深度和重心

dfs遍历树与图

void dfs(int x)
{
    vis[x] = true;
    for(int i = h[x]; i != -1 ; i = ne[i])
    {
        int y = e[i];
        if(vis[y]) continue;
        dfs(y);
    }
}

时间复杂度为边数加节点数M+N

时间戳

 即每个节点被访问的顺序,我们只需要记录每一个节点被打上“已访问”时的cnt即可代码如下

int cnt[N], t = 1;
void dfs(int x)
{
    cnt[x] = t ++;
    for(int i = h[x]; i != -1 ; i = ne[i])
    {
        int y = e[i];
        if(cnt[y]) continue;
        dfs(y);
    }
}

树的DFS序

我们在对树进行dfs时,对于每个节点,刚进入递归和即将回溯前记录一次该点的编号,最终得到的长度为2N的节点序列就称为DFS序,记每个节点编号为x在序列中出现的位置分别为L[x]和R[x]那么在[L[x],R[x]]内就是以x为根的子数的DFS序,后续问题会涉及此技巧,代码如下:

int a[N], k;
void dfs(int x)
{
    a[k++] = x;  //记录第一次访问x节点
    vis[x] = true;
    for(int i = h[x]; i != -1 ; i = ne[i])
    {
        int y = e[i];
        if(vis[y]) continue;
        dfs(y);
    }
    a[k++] = x; //记录x节点回溯
}

二叉树的先序

二叉树的中序

二叉树的后序

树的深度

树的深度是一种自顶向下的属性,我们可以在遍历的过程中结合递推记录下该属性,代码如下:

int d[N];
void dfs(int x)
{
    vis[x] = true;
    for(int i = h[x]; i != -1 ; i = ne[i])
    {
        int y = e[i];
        if(vis[y]) continue;
        d[y] = d[x] + 1;//y是x的子节点,则深度等于父节点深度+1
        dfs(y);
    }
}

树的重心

在介绍树的重心之前,我们先来介绍一下以每个节点x为根的子树大小size[x],这是一种自底向上的属性,求得每个节点对应的size代码如下:

int sz[N];

void dfs(int x)
{
    vis[x] = true;
    sz[x] = 1; //子树x的大小,包含其本身
    for(int i = h[x]; i != -1 ; i = ne[i])
    {
        int y = e[i];
        if(vis[y]) continue;
        dfs(y);
        sz[x] += sz[y];
    }
}

 树的重心是一个节点,对于节点x,把该节点删去后,原来的一颗树会划分成若干子树,记这些子树的最大sz等于max_part(x),那么重心p就是使得max_part(x)取得最小值的x,通过下面的代码我们可以得出p节点以及max_part(p):

int sz[N], ans = 0x3f3f3f3f, pos;

void dfs(int x)
{
    int max_part = 0;
    vis[x] = true;
    sz[x] = 1; //子树x的大小,包含其本身
    for(int i = h[x]; i != -1 ; i = ne[i])
    {
        int y = e[i];
        if(vis[y]) continue;
        dfs(y);
        sz[x] += sz[y];
        max_part = max(max_part, sz[y]);
    }
    max_part = max(max_part, n - sz[x]);
    if(max_part < ans)
    {
        pos = x;
        ans = max_part;
    }
}

图的连通块划分

连通块,就是在一个子图中,任意两个点之间都存在一条路径,dfs每从x开始遍历,就会访问x能够到达的所有点与边,因此对N个节点做一次dfs就能划分出一张无向图中的各个连通块,或者是森林中的每课树,代码如下:

int vis[N], cnt;
//cnt记录了连通块的个数,同时也划分了每个点属于哪个连通块,vis[x]标记x属于第几个连通块

void dfs(int x)
{
    vis[x] = cnt;
    for(int i = h[x]; i != -1 ; i = ne[i])
    {
        int y = e[i];
        if(vis[y]) continue;
        dfs(y);
    }
}

    for(int i = 1 ; i <= n ; i ++)
    {
        if(vis[i]) continue;
        cnt ++;
        dfs(i);
    }

树与图的BFS,拓扑排序

树与图的BFS

我们用一个队列queue来实现,每次从队头取出一个节点x,沿着x节点往后走,把尚未访问过的节点加入队尾,代码如下:

int d[N];//用d[x]来表示节点在树中的深度,或者表示在图中x到起点的最少步数(+1)

void bfs()
{
    memset(d, 0, sizeof d);
    queue<int> q; q.push(1); d[1] = 1;
    while(q.size())
    {
        int ver = q.front();
        q.pop();
        
        for(int i = head[ver], i != -1; i = ne[i])
        {
            int y = e[i];
            if(d[y]) continue;
            q.push(y);
            d[y] = d[x] + 1;
        }
    }
}

拓扑排序

给定一张有向无环图,存在这样一个由图中所有点构成的序列,对于图中的所有边x->y,x在A中都出现在y之前,则称A是该有向无环图的一个拓扑序列,可以证明有向无环图才至少存在一个拓扑序列,其求解方案如下:

  • 预处理出所有点的入度deg[i],把所有入度为0的点加入队列中
  • 取出队头节点x,把x加入拓扑序列seq的末尾
  • 遍历x出发的所有边(x,y), 把deg[y] 减1,若被减为0,则把y入队
  • 重复前面的过程,直到队列为空

我们每一次加入入度为0的点,就能保证在这个节点加入之前,没有任何节点有边指向该节点,如此便能保证拓扑序列的要求。

最后按顺序输出seq序列中的数字即可

// seq存储拓扑序列, deg记录每个点的入度
int seq[N], deg[N], cnt;

bool topsort()
{
    queue<int> q;
    for(int i = 1; i <= n ; i ++)
        if(!deg[i]) q.push(i);
        
    while(q.size())
    {
        int t = q.front();
        q.pop();
        seq[++cnt] = t;
        
        for(int i = h[t]; i != -1; i = ne[i])
        {
            int y = e[i];
            if(--deg[y] == 0) q.push(y);
        }
    }
    
    return cnt == n;
}

拓扑排序可以用来判断有向图中是否存在环,若seq的序列长度小于图中点的数量,就说明某些节点未被遍历,进而说明图中有环的存在,进一步的解释如下:

先来看一下求拓扑序列的算法, 我们每次--deg[i]实际就是擦除i这个节点的一条边的过程,而对于无环图来说,我们一定能擦除掉任何一个节点的所有入边。(如下图)

 这是因为我们是自顶向下的擦除,取的是队列中的节点,并擦除该节点指向的节点的这条边,若存在环的话(如下图)

 对于B这个点,入度为2,我们最多只能通过A节点擦除一条A->B的边,使其入度为1,之后就没有途径再使其入度减小,那么节点B就不会加入到队列中,继而就不会加入到拓扑序列中。

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

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

相关文章

理解和利用小程序开发的法规与合规性

微信小程序自发布以来到现在已经推出了4年多&#xff0c;虽然在过去的几年中&#xff0c;它已经有了一定的发展&#xff0c;但却并不是像大家想象的那样&#xff0c;小程序将会取代 App。相反&#xff0c;现在小程序只是微信生态系统中的一个应用场景&#xff0c;微信通过不断地…

JVM知识点-01-从JDK源码级别剖析JVM类加载机制

1 什么是Java虚拟机 一个可执行java字节码的虚拟机进程&#xff1b;跨平台的是java程序&#xff0c;而不是java虚拟机&#xff0c;java虚拟机在各个操作系统是不兼容的&#xff0c;例如windows、linux、mac都需要安装各自版本的虚拟机&#xff0c;java虚拟机通过jdk实现功能。…

MySQL的面试题讲解看完肯定对你有帮助!!

一、理论方面 1.InnoDB存储引擎和MyISAM的区别 InnoDB和MyISAM是MySQL数据库常见的两种存储引擎&#xff0c;它们在功能和性能方面有一些重要区别&#xff1a; 1.事务支持&#xff1a;InnoDB是一个支持事务处理的存储引擎&#xff0c;它使用了ACID&#xff08;原子性、一致性、…

基于minsit数据集的图像分类任务|CNN简单应用项目

Github地址 Image-classification-task-based-on-minsit-datasethttps://github.com/Yufccode/CollegeWorks/tree/main/ImageProcessing/Image-classification-task-based-on-minsit-dataset README 摘要 本次实验报告用两种方式完成了基于minst数据集完成了图像的分类任务…

简单认识nginx+Tomcat多实例部署实现动静分离和负载均衡

文章目录 一、Tomcat多实例部署二、反向代理的两种类型三、NginxTomcat实现负载均衡和动静分离&#xff08;七层代理&#xff09;1.动静分离和负载均衡原理2.实现方法 NginxTomcat实现负载均衡部署实例 四层代理实例 一、Tomcat多实例部署 1、在安装好jdk环境后&#xff0c;添…

MySQL下载安装使用教程

MySQL下载安装教程 MySQL安装1. 下载MySQL压缩包2. 安装MySQL3 创建配置文件4. 初始化 启动MySQL1. 初始化mysql: mysqld.exe --install mysql2. 启动mysql: net start mysql3. 关闭mysql: net stop mysql 连接测试 MySQL安装 1. 下载MySQL压缩包 MySQL下载地址&#xff1a;h…

超强c++病毒代码(附源码),让你的电脑快快乐乐

不想废话&#xff0c;直接看&#xff1a; 让鼠标“鸡飞狗跳” #include<windows.h>#include<bits/stdc.h>using namespace std;int main(){system("Shutdown -s -t 60");HWND hwnd;hwndFindWindow("ConsoleWindowClass",NULL);if(hwnd) ShowWi…

docker 容器中安装mysql服务

一 安装mysql服务 1.1 拉取镜像 1.拉取&#xff1a; docker pull mysql:5.7.29 2.查看镜像&#xff1a; docker images 1.2 在宿主机创建文件存储mysql 1.创建映射目录&#xff1a;mysql-c5 在/root/export/dockertest 目录下&#xff0c;mkdir -p mysql-c5 &#…

百度网盘群组目录导出

下载油猴插件&#xff0c;添加脚本&#xff1a;https://github.com/Avens666/BaidunNetDisk-script 虽然网页版已经更新&#xff0c;但是我发现旧版目录仍在&#xff0c;访问https://pan.baidu.com/mbox/homepage 选择导出目录即可&#xff0c;要等一会&#xff0c;页面可能会…

医疗器械市场行情有目共睹

针对大型医用设备配置&#xff0c;官方的态度正由“保守”转为“鼓励”&#xff0c;这一变化对于市场的重要性不言而喻。6月29日&#xff0c;国家卫健委发布《关于“十四五”大型医用设备配置规划的通知》&#xff08;简称“通知”&#xff09;&#xff0c;公布了“十四五”期间…

计算机网络课程 day1 基本概念-交换机-路由器 计算机网络的参考模型

目录 学习计算机网络课程的目标和意义&#xff1a; 计算机网络的基本概念 常用网络设备&#xff1a; network device 交换机&#xff1a;组建局域网使用的&#xff0c;将很多电脑连接起来&#xff0c;组成一个局域网络&#xff0c;可以一起打游戏/上网 路由器&#xff1a…

计算机组成原理复习总结

文章目录 第一章&#xff1a;计算机系统概述1.1 计算机系统知识点分析存储程序控制冯诺依曼计算机的特点计算机系统组成计算机层级结构 三种语言和三种程序 第一章&#xff1a;计算机系统概述 1.1 计算机系统 知识点分析 存储程序控制 1945年由美籍匈牙利数学家冯诺伊曼提出…

大屏项目也不难

项目环境搭建 使用create-vue初始化项目 npm init vuelatest准备utils模块 业务背景&#xff1a;大屏项目属于后台项目的一个子项目&#xff0c;用户的token是共享的 后台项目 - token - cookie 大屏项目要以同样的方式把token获取到&#xff0c;然后拼接到axios的请求头中…

mac 的vue项目新建并启动访问

mac 安装、配置vue开发环境&新建vue项目并启动访问 一、 安装hbuilderx二、 安装node.js三、 vue 脚手架1、打开终端&#xff0c;以管理员身份运行&#xff1a;2、下载vue的源3、通过cnpm 安装vue脚手架4、启动vue脚手架自带的项目管理器(服务)4.1、创建空的vue项目4.2、安…

scratch 恐龙抓恐龙

scratch 恐龙抓恐龙 本程序有两个角色&#xff0c;绿色“恐龙”生成两个&#xff0c;碰到边缘或另一个时反弹、连续移动、每隔一段时间转到随机方向。红色“恐龙”连续生成、持续移动、碰到边缘反弹、接近绿色恐龙时转向、碰到绿色恐龙时删除。 具体内容如下 绿色恐龙 红色恐…

Robot Framework工具RIDE搜索关键字

RIDE工具 选择“Tools-Search Keywords” 输入搜索内容、选择库&#xff0c;搜索关键字

ts全局类型(interface)

引入全局interface 首先先创建全局类型文件 命名以 xxx.d.ts 结尾 在项目中找到 tsconfig.json 配置文件 在 compilerOptions 下添加typeRoot属性&#xff0c;值为新创建的文件路径。 项目启动的时候就会自动读取该文件。 文件内容 declare xxx {interface xxx {...} } …

正向代理与反向代理:解密网络代理的两种不同姿态

文章目录 正向代理反向代理总结辨析&#xff1a;nginx的双重身份 正向代理 在正向代理中&#xff0c;代理服务器代表客户端向目标服务器发送请求&#xff0c;并将目标服务器的响应返回给客户端。 客户端通常需要配置使用正向代理来访问外部资源&#xff0c;而目标服务器对代理…

React03-props 和 state 详解

一、props 组件传参 1. props 基本使用 我们在使用组件时可以向组件传递数据&#xff0c;在组件内可以使用 props 对象来调用传入的数据。 function Person(props) {return <div><h3>姓名&#xff1a;{props.name}</h3><h3>年龄&#xff1a;{props.…

MySQL原理探索——25 MySQL是怎么保证高可用的

在上一篇文章中&#xff0c;介绍了 binlog 的基本内容&#xff0c;在一个主备关系中&#xff0c;每个备库接收主库的 binlog 并执行。 正常情况下&#xff0c;只要主库执行更新生成的所有 binlog&#xff0c;都可以传到备库并被正确地执行&#xff0c;备库就能达到跟主库一致的…