【1462. 课程表 IV】

news2024/11/26 5:27:30

来源:力扣(LeetCode)

描述:

你总共需要上 numCourses 门课,课程编号依次为 0numCourses-1 。你会得到一个数组 prerequisite ,其中 prerequisites[i] = [ai, bi] 表示如果你想选 bi 课程,你 必须 先选 ai 课程。

  • 有的课会有直接的先修课程,比如如果想上课程 1 ,你必须先上课程 0 ,那么会以 [0,1] 数对的形式给出先修课程数对。

先决条件也可以是 间接 的。如果课程 a 是课程 b 的先决条件,课程 b 是课程 c 的先决条件,那么课程 a 就是课程 c 的先决条件。

你也得到一个数组 queries ,其中 queries[j] = [uj, vj]。对于第 j 个查询,您应该回答课程 uj 是否是课程 vj 的先决条件。

返回一个布尔数组 answer ,其中 answer[j] 是第 j 个查询的答案。

示例 1:

1

输入:numCourses = 2, prerequisites = [[1,0]], queries = [[0,1],[1,0]]
输出:[false,true]
解释:课程 0 不是课程 1 的先修课程,但课程 1 是课程 0 的先修课程。

示例 2:

输入:numCourses = 2, prerequisites = [], queries = [[1,0],[0,1]]
输出:[false,false]
解释:没有先修课程对,所以每门课程之间是独立的。

示例 3:

3

输入:numCourses = 3, prerequisites = [[1,2],[1,0],[2,0]], queries = [[1,0],[1,2]]
输出:[true,true]

提示:

  • 2 <= numCourses <= 100
  • 0 <= prerequisites.length <= (numCourses * (numCourses - 1) / 2)
  • prerequisites[i].length == 2
  • 0 <= ai, bi <= n - 1
  • ai != bi
  • 每一对 [ai, bi] 都 不同
  • 先修课程图中没有环。
  • 1 <= queries.length <= 104
  • 0 <= ui, vi <= n - 1
  • ui != vi

方法一:广度优先搜索 + 拓扑排序

思路与算法

题目给出 numCourses 门课(编号从 0 开始),并给出了一个长度为 n 的课程之间的制约关系数组 prerequisite ,其中 prerequisite[i] = [ai, bi] 表示在学习课程 bi 之前必须要完成课程 ai 的学习,即课程 ai 为 bi 的先决条件。我们可以将上述条件构建一张有向图——将每一个课程看作一个点(课程编号即为点的编号),每一个制约关系 prerequisite[i] = [ai,bi] 对应一条从点 ai 指向 bi 的有向边,并且题目保证了这样构建出来的图不存在环。

现在有 m 个查询 queries,其中对于第 i 个查询 queries[i] = [ui,vi],我们需要判断课程 ui 是否是课程 vi 的直接或间接先决条件。我们创建一个 numCourses×numCourses 的矩阵 isPre,其中 isPre[x][y] 表示课程 x 是否是课程 y 的直接或间接先决条件,若是则 isPre[x][y] = True,否则 isPre[x][y] = False。在完成 isPre 计算后,我们对于每一个查询就可以在 O(1) 时间得到结果。对于 isPre 的计算,我们可以通过「广度优先搜索」+「拓扑排序」来对矩阵 isPre 进行计算:

首先我们需要计算有向图中每一个节点的入度,并对入度为 0 的节点加入队列。然后只要队列非空,就进行以下操作:

  • 取出队列队首元素,同时,将这个节点及其所有前置条件节点设置为所有后续节点的前置条件节点,然后对每一个后续节点入度进行 −1 操作,若操作后的节点入度为 0,则继续将该节点加入队列。

「拓扑排序」结束后,矩阵 isPre 计算完毕,然后我们遍历每一个查询,根据矩阵 isPre 即可得到每一个查询的结果。

代码:

class Solution {
public:
    vector<bool> checkIfPrerequisite(int numCourses, vector<vector<int>>& prerequisites, vector<vector<int>>& queries) {
        vector<vector<int>> g(numCourses);
        vector<int> indgree(numCourses, 0);
        vector<vector<bool>> isPre(numCourses, vector<bool>(numCourses, false));
        for (auto& p : prerequisites) {
            ++indgree[p[1]];
            g[p[0]].push_back(p[1]);
        }
        queue<int> q;
        for (int i = 0; i < numCourses; ++i) {
            if (indgree[i] == 0) {
                q.push(i);
            }
        }
        while (!q.empty()) {
            auto cur = q.front();
            q.pop();
            for (auto& ne : g[cur]) {
                isPre[cur][ne] = true;
                for (int i = 0; i < numCourses; ++i) {
                    isPre[i][ne] = isPre[i][ne] | isPre[i][cur];
                }
                --indgree[ne];
                if (indgree[ne] == 0) {
                    q.push(ne);
                }
            }
        }
        vector<bool> res;
        for (auto& query : queries) {
            res.push_back(isPre[query[0]][query[1]]);
        }
        return res;
    }
};

时间 232ms 击败 46.33%使用 C++ 的用户
内存 58.08MB 击败 61.78%使用 C++ 的用户
复杂度分析

  • 时间复杂度:O(numCourses2+n+m),其中 numCourses 为课程数,n 为题目给出的先决条件的数目,m 为题目给出的查询数,其中通过计算矩阵 isPre 的时间复杂度为 O(numCourses2),构建有向图的复杂度为 O(numCourses+n),处理每一个查询的复杂度为 O(1),共有 m 个查询,所以总的查询时间复杂度为 O(m)。
  • 空间复杂度:O(numCourses2+n),其中 numCourses 为课程数,n 为题目给出的先决条件的数目,主要为构建有向图和矩阵 isPre 的空间开销。注意返回值不计入空间复杂度。

方法二:深度优先搜索 + 拓扑排序

思路与算法

「方法一」中「拓扑排序」的实现同样可以通过「深度优先搜索」来实现。与「广度优先搜索」计入每一个点的出度不同,通过「深度优先搜索」需要记录每一个点是否被访问,我们用 vi[x] 来表示课程 x 是否被访问,初始时为 False。

我们从编号小到大遍历全部节点,若节点 i 未被访问,则进入「深度优先搜索」流程:

  • 若当前节点 x 已被访问,则直接返回。
  • 若当前节点 x 未被访问,将访问状态设为已访问,然后继续对其全部后继节点递归进行「深度优先搜索」流程。将节点 x 置为其每一个后继节点 y 的先决条件,有 isPre[x][y] = True,以及对于每一个以 y 为先决条件的节点 t,节点 x 同样为 t 的先决条件,有 isPre[x][t] = True。

遍历完成后,「拓扑排序」完成,矩阵 isPre 计算完毕,然后我们遍历每一个查询,根据矩阵 isPre 即可得到每一个查询的结果。

代码:

class Solution {
public:
    void dfs(vector<vector<int>>& g, vector<vector<bool>>& isPre, vector<bool>& vi, int cur) {
        if (vi[cur]) {
            return;
        }
        vi[cur] = true;
        for (auto& ne : g[cur]) {
            dfs(g, isPre, vi, ne);
            isPre[cur][ne] = true;
            for (int i = 0; i < isPre.size(); ++i) {
                isPre[cur][i] = isPre[cur][i] | isPre[ne][i];
            }
        }
    }

    vector<bool> checkIfPrerequisite(int numCourses, vector<vector<int>>& prerequisites, vector<vector<int>>& queries) {
        vector<vector<int>> g(numCourses);
        vector<bool> vi(numCourses, false);
        vector<vector<bool>> isPre(numCourses, vector<bool>(numCourses, false));
        for (auto& p : prerequisites) {
            g[p[0]].push_back(p[1]);
        }
        for (int i = 0; i < numCourses; ++i) {
            dfs(g, isPre, vi, i);
        }
        vector<bool> res;
        for (auto& query : queries) {
            res.push_back(isPre[query[0]][query[1]]);
        }
        return res;
    }
};

时间 212ms 击败 55.98%使用 C++ 的用户
内存 57.84MB 击败 73.75%使用 C++ 的用户
复杂度分析

  • 时间复杂度:O(numCourses2+n+m) ,其中 numCourses 为课程数,n 为题目给出的先决条件的数目,m 为题目给出的查询数,其中计算矩阵 isPre 的时间复杂度为 O(numCourses2) ,构建有向图的复杂度为 O(numCourses+n),处理每一个查询的复杂度为 O(1),共有 m 个查询,所以总的查询时间复杂度为 O(m)。
  • 空间复杂度:O(numCourses2+n) ,其中 numCourses 为课程数,n 为题目给出的先决条件的数目,主要为构建有向图和矩阵 isPre 的空间开销。注意返回值不计入空间复杂度。
    author:力扣官方题解

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

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

相关文章

你真的会报bug吗?常见10条错误

几乎每一位测试员&#xff0c;都会因为自己发现了一个bug而沾沾自喜&#xff0c;然后迫不及待地报bug。此时&#xff0c;恨不得有个喇叭&#xff0c;在整个办公室广而告之一下&#xff0c;实际上这样的行为并不可取&#xff0c;因为软件开发人员最怕的就是思路被打断&#xff0…

关于vue封装form表单单向流数据问题

vue在封装form表单业务组件问题时&#xff0c;传参的方式可能是 组件接收的形式这样很容易打破vue 的单向流数据规则&#xff0c;这样写肯定不会影响功能&#xff0c;只不过代码离屎山越来越近。 创建一个计算属性&#xff0c;get获取form 再利用 proxy 代理去代理这个对象&am…

DBC文件解析

一.candb 二.DBC文件解析 NS_ : NS_DESC_&#xff1a;用于描述网络信号的描述信息。 CM_&#xff1a;用于定义信号的描述信息。 BA_DEF_&#xff1a;定义信号的属性。 BA_&#xff1a;为信号属性定义值。 VAL_&#xff1a;为信号的枚举值定义标签。 CAT_DEF_&#xff1a;定义…

SoftwareTest2 - 软件测试相关概念

软件测试答疑篇 目标一 . 什么是需求二 . 测试用例三 . 什么是 BUG四 . 开发模型4.1 软件的生命周期需求分析计划设计编码测试运行维护 4.2 软件测试的生命周期需求分析测试计划测试设计与开发执行测试测试评估 4.3 常见模型瀑布模型螺旋模型增量模型、迭代模型敏捷模型scrum模…

GO语言篇之embed

GO语言篇之embed 文章目录 GO语言篇之embed前言目录结构文件转[]byte文件转string多文件转embed.FS目录转embed.FS文件和目录组合的方式转embed.FS 前言 embed是Go语言提供的一种机制&#xff0c;可使静态文件或文件夹嵌入Go语言程序中&#xff0c;使我们Go语言的可执行文件包…

kibana报错内存溢出问题解决

一、背景&#xff1a; kibana内存溢出&#xff0c;进程被kill掉&#xff0c;导致前端页面访问不到。 报错内容 二、报错原因&#xff1a; 发现是前端 js 报的内存 oom 异常&#xff0c;通过网上资料发现node.js 的默认内存大小为1.4G Node 中通过 JavaScript 使用内存时只能…

PCL入门(六):深度图提取边界

目录 1. 深度图介绍2. 深度图生成3. 边界提取 1. 深度图介绍 参考《02-深度图》 深度图像&#xff08;Depth Images&#xff09;也被称为距离影像&#xff08;Range Image&#xff09;&#xff0c;是指将从图像采集器到场景中各点的距离值作为像素值的图像&#xff0c;它直接…

CentOS7上从0开始搭建Zookeeper集群

CentOS7上搭建Zookeeper集群 环境准备安装jdk安装zookeeper下载zookeeper解压zookeeper修改zookeeper配置文件 搭建zookeeper集群修改zoo.cfg文件添加myid文件启动zookeeper集群 环境准备 首先你需要准备三台zookeeper&#xff08;待会会讲zookeeper的安装流程&#xff09;&am…

运算放大器典型应用(二)

文章目录 十、采样保持电路十一、有源滤波电路二阶有源低通滤波器问题二阶截至频率如何算 十、采样保持电路 十一、有源滤波电路 给单片机供电R一般大于4.1Ω小于10Ω&#xff0c;太大会产生功耗 二阶有源低通滤波器问题 二阶截至频率如何算 通频带比较窄可以用这种&#xff0…

IDEA启动时选择项目

IDEA默认情况下&#xff0c;启动时会选择上一次打开的项目继续。如果我们希望每次启动时都需要手动选择项目&#xff0c;可以按照下列顺序修改&#xff1a; 【File】-【Settings】-【Apperance&Behavior】-【System Settings】-【Startup/Shutdown】 取消选中Reopen last…

硬件总线基础07:PCIe总线基础-事务层(2)

说在开头&#xff1a;关于哲学 在《东邪西毒》电影里欧阳锋说&#xff1a;“看来你的年纪也有四十出头了&#xff0c;这四十多年来&#xff0c;总有些事你是不愿再提&#xff0c;或是有些人不想再见&#xff0c;有的人曾经对不起你&#xff0c;也许你想过杀了他们&#xff0c;…

PyCharm Clion IDEA专业版安装图文教程

1.下载专业版 PyCharm Download PyCharm: Python IDE for Professional Developers by JetBrains 2.以2023.1.4为例 3.next 4.next 5.next 6.Install 7.Finish 8.Activate 链接&#xff1a;https://pan.baidu.com/s/1N9n8wGgkvjfOX8oDrfX2Hw 提取码&#xff1a;yyds

知识图谱实战应用28-基于py2neo的ICD-11疾病分类的知识图谱的查询与问答实战应用

大家好,我是微学AI,今天给大家介绍一下知识图谱实战应用28-基于py2neo的ICD-11疾病分类的知识图谱的查询与问答实战应用。使用基于py2neo的ICD-11疾病分类知识图谱,我们能够像探索一座生物医学宇宙般,穿梭在各种疾病之间。这个神奇的图谱可以帮助我们揭示各种疾病之间复杂而…

1014. 最佳观光组合

1014. 最佳观光组合 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a; 原题链接&#xff1a; 1014. 最佳观光组合 https://editor.csdn.net/md/?not_checkout1&spm1000.2115.3001.5352 完成情况&#xff1a; 解题思路&#xff1a; …

如何把视频格式转换成mp4?支持的格式种类非常多。

如何把视频格式转换成mp4&#xff1f;随着计算机技术的迅猛发展&#xff0c;我们现在有着各种各样的视频格式可供选择&#xff0c;平时我们都知道的mp4、flv、mov、mkv、avi、wmv等&#xff0c;都是视频格式的种类。其中&#xff0c;MP4是一种具有极佳兼容性的视频格式&#xf…

Golang goroutine 进程、线程、并发、并行

goroutine 看一个需求 需求&#xff1a;要求统计1-200000000000的数字中&#xff0c;哪些是素数? 分析思路&#xff1a; 1)传统的方法&#xff0c;就是使用一个循环&#xff0c;循环的判断各个数是不是素数&#xff08;一个任务就分配给一个cpu去做&#xff0c;这样很不划算…

光谱通用款积分球

随着惯性约束聚变&#xff08;ICF&#xff09;物理理论的不断发展以及精密物理实验要求的不断提高&#xff0c;激光驱动器的光束路数急剧增多&#xff0c;光路长度和元器件数目成倍增长。模块化是新一代激光驱动器的发展趋势。对于高功率激光多参数测量系统&#xff0c;模块化设…

解决2K/4K高分屏下Vmware等虚拟机下Kail Linux界面显示问题

问题现象 在我们日常使用VirtualBox、Vmware workstation、Hyper-V等虚拟机安装使用Kali系统&#xff0c;在2K/4K高分辨率电脑下Kali系统界面显示太小&#xff0c;包括各种软件及命令终端字体均无法很直观的看出&#xff0c;影响我们的正常测试及使用。 常规处理思路 很多人…

Vue3通过 directive 实现 el-dropdown下拉菜单项最小宽度等于内容宽度

文章目录 1. 初始效果与最终效果2. 分析3. 解决思路&#xff1a;directives4. 代码实现小结 1. 初始效果与最终效果 原始效果最终效果 2. 分析 el-dropdown API 并不提供配置项让我们实现下拉菜单项最小宽度等于内容宽度&#xff0c;但我们能发现它提供了 popper-class 用于自…

Spring学习|使用JavaConfig实现bean配置、代理模式:静态代理模式、动态代理模式(通俗易懂)

使用JavaConfig实现bean配置 正常我们在spring容器中注册一个Bean,我们需要去bean.xml中去配置&#xff0c;但是我们也可以用JavaConfig类&#xff0c;来去充当bean.xml的作用 首先&#xff0c;我们创建一个User类&#xff0c;Component代表他是一个bean,方便让spring容器来扫…