图论:1203. 项目管理(以小组为单位进行拓扑排序)

news2024/9/21 2:35:59

文章目录

  • 1.问题分析
  • 2.思路整理
  • 3.官解思路

LeetCode:1203. 项目管理
在这里插入图片描述
在这里插入图片描述
建议直接看思路整理

1.问题分析

仔细读题可以发现,如果不考虑小组项目彼此相邻,则项目之间的依赖关系就是一个拓扑排序。

但是如果要考虑小组项目彼此相邻,问题就复杂起来了。

容易想到两种策略,第一种就是前期处理,处理拓扑图使得小组也成为一个约束条件,保证一个小组一个小组输出拓扑序;第二种是后期处理,先求出拓扑序然后按小组进行分类,然后再判断该拓扑序是否合法(该方法复杂度过高)。

我们来看第一种策略,很容易想到将小组也作为一个顶点,其后继是所有它管理的项目,这样轮到该小组,该小组的项目才能进行。而然要使得按一个小组一个小组来,那么前一个小组在输出拓扑序未输出完时,后一个小组不能输出,相当于前一个小组制约了后一个小组。这样的话怎么做呢?

我们知道仅仅加一个小组是不行的,因为其入度为0,导致顺序又是乱的,那我们是否可以用前面一个小组的每个项目都连接到后一个小组,这样的话前面的小组的每一个项目完成了才能进行后面的小组,后面这个小组才能开放自己的项目。如果有多个小组可以进行,那么它们一定互不影响,因为根据前面的约束条件,已经没有其他小组的项目可以约束它,我们将其依次放入一个栈中,以便于之后一个一个开放。并且只能在没有可进行的时候开放,原因在于,可能有的没有小组约束,它们进行完成之后,再考虑小组的,不然也会产生顺序问题。

考虑了这个问题后,一个小组的前驱已经被解决了,但是小组的后继呢? 这你可能会觉得有点矛盾,但并不是这样,小组的约束解决了。但是没有小组的项目也是需要约束的,因为它相当于自己作为自己的小组。

实现如下:时间复杂度: O ( n + m ) O(n+m) O(n+m)
在这里插入图片描述

class Solution {
public:
    vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) {
        vector<int> result;//存储最终结果
        stack<int> sta; // 存储可开放的小组
        queue<int> q;
        //预处理,将小组进行约束
        vector<vector<int>> edges(n + m); //edges[i]表示其后继
        vector<int> inDegrees(n + m, 0);//入度表示约束条件,小组的编号增加偏移m

        //求入度 并构造 边集
        for(int i = 0; i < beforeItems.size(); ++ i){
            vector<int> & pre = beforeItems[i];
            //pre 是 i的前驱,pre的小组也必然是i小组的前驱(除非相同)
            for(auto & p : pre){
                edges[p].emplace_back(i);
                inDegrees[i] ++;
                if(group[i] != - 1 && group[p] != group[i]){//任何对项目的约束,也都转化成小组之间的约束
                    if(group[p] != -1){
                        edges[group[p] + n].emplace_back(group[i] + n);
                        inDegrees[group[i] + n] ++;
                    }
                }
            }
        }
        //建立小组对项目的约束
        for(int i = 0; i < group.size(); ++ i){
            if(group[i] != -1){
                edges[group[i] + n].emplace_back(i);
                inDegrees[i] ++;
            }
        }

        //求入度为0
        for(int i = 0; i < inDegrees.size(); ++ i){
            if(inDegrees[i] == 0){
                if(i < n)
                    q.push(i);
                else sta.push(i);
            }
        }

        //构建组内成员对后继项目的共同约束
        unordered_set<int> st;
        for(int i = 0; i < beforeItems.size(); ++ i){
            for(auto & pre : beforeItems[i]){
                if(group[i] == -1 && group[pre] != -1 && st.count(group[pre]) == 0){
                    st.insert(group[pre]);
                    for(auto & v : edges[group[pre] + n]){
                        edges[v].emplace_back(i);
                        inDegrees[i] ++;
                    }
                }
            }
        }

        while(!sta.empty() || !q.empty()){
            if(q.empty()){//只能开放小组了
                q.push(sta.top()); sta.pop();
            }
            while(!q.empty()){
                int u = q.front();q.pop();
                if(u < n) result.emplace_back(u);
                for(auto & v : edges[u]){
                    inDegrees[v] --;
                    if(inDegrees[v] == 0){
                        if(v < n) {q.push(v);}
                        else sta.push(v);
                    }
                }
            }
        }
        if(result.size() != n) return vector<int>{};
        return result;
    }
};

如果将单个项目也当成小组,我们可以进一步优化,对于可以执行的单个项目,约束它的所有项目都已经执行完毕,我们在任何时候执行都可以,由于它也当做小组,为了避免它和可以执行的其他小组冲突,我们将其也入栈,直到无可奈何才执行它。这样就不用创建对于它的约束了。
在这里插入图片描述

class Solution {
public:
    vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) {
        vector<int> result;//存储最终结果
        stack<int> sta; // 存储可开放的小组
        queue<int> q;
        //预处理,将小组进行约束
        vector<vector<int>> edges(n + m); //edges[i]表示其后继
        vector<int> inDegrees(n + m, 0);//入度表示约束条件,小组的编号增加偏移m

        //求入度 并构造 边集
        for(int i = 0; i < beforeItems.size(); ++ i){
            vector<int> & pre = beforeItems[i];
            //pre 是 i的前驱,pre的小组也必然是i小组的前驱(除非相同)
            for(auto & p : pre){
                edges[p].emplace_back(i);
                inDegrees[i] ++;
                if(group[i] != - 1 && group[p] != group[i]){//任何对项目的约束,也都转化成对小组的约束
                    edges[p].emplace_back(group[i] + n);
                    inDegrees[group[i] + n] ++;
                }
            }
        }
        //建立小组对项目的约束
        for(int i = 0; i < group.size(); ++ i){
            if(group[i] != -1){
                edges[group[i] + n].emplace_back(i);
                inDegrees[i] ++;
            }
        }

        //求入度为0
        for(int i = 0; i < inDegrees.size(); ++ i){
            if(inDegrees[i] == 0){
                if(i < n)
                    q.push(i);
                else sta.push(i);
            }
        }

        while(!sta.empty() || !q.empty()){
            if(q.empty()){//只能开放小组了
                q.push(sta.top()); sta.pop();
            }
            while(!q.empty()){
                int u = q.front();q.pop();
                if(u < n) result.emplace_back(u);
                for(auto & v : edges[u]){
                    inDegrees[v] --;
                    if(inDegrees[v] == 0){
                        if(v < n && group[v] != -1) {q.push(v);}
                        else sta.push(v);
                    }
                }
            }
        }
        if(result.size() != n) return vector<int>{};
        return result;
    }
};

2.思路整理

这样一个困难题被思考出来感觉非常的不可思议。

思路的主要内容是:

  • 给小组增加编号,小组也当做图中的顶点,小组可以执行了,整个小组项目才可以执行,这样保证了小组项目的约束一致性。
  • 小组之间存在约束条件,并且对于两个项目 ( u , v ) (u,v) (u,v)而言,它们的小组也存在约束即 u u u的小组要先于 v v v的小组项目执行。
    • 如果不对小组进行约束的话,小组入度为0,小组的增加形同虚设
    • 我们找到了两个项目 ( u , v ) (u,v) (u,v)的约束关系,同时找到了小组的约束关系,前一个小组进行之后,后一个小组才能进行。
  • 我们对小组延迟进行执行,将其单独放入一个中,只有拓扑排序时队列中没有可执行项目时,才从中取出一个小组进行执行,这样保证了是一个一个小组进行的,并且这个小组的顺序是满足小组之间的约束条件的。
  • 对于无小组的项目,我们当做是一个项目的小组即可,并且我们只需要在其能运行时将其入保证其不影响当前组的项目顺序即可,这样它执行时,一定是其他小组完成时,而不会插在其他小组中间执行。

总体来看,这个思路和官解思路没啥区别,实际上也是以小组为单位进行拓扑排序的一种方式。只不过我是增加一个数据结构 和 增加小组之间约束条件 来保证这一点的。

  • 小组之间的约束条件,加上保证正在执行的小组必须完整的执行完,保证了以小组为单位进行拓扑排序。
        //求入度 并构造 边集
        for(int i = 0; i < beforeItems.size(); ++ i){
            vector<int> & pre = beforeItems[i];
            //pre 是 i的前驱,pre的小组也必然是i小组的前驱(除非相同)
            for(auto & p : pre){
                edges[p].emplace_back(i);
                inDegrees[i] ++;
                if(group[i] != - 1 && group[p] != group[i]){//任何对项目的约束,也都转化成小组之间的约束
                    if(group[p] != -1){
                        edges[group[p] + n].emplace_back(group[i] + n);
                        inDegrees[group[i] + n] ++;
                    }
                }
            }
        }

3.官解思路

官解实际上就是先求出小组之间的拓扑序,然后对组内拓扑排序,然后把没有小组的项目单独成组,最后按照组的拓扑排序依次进行执行。和我的思路本质是一样的。
在这里插入图片描述

以小组为单位的拓扑排序是个好题,它将拓扑排序的深度提升到了新高度。关键在于小组约束小组成员,单项目单独成组,小组之间按顺序拓扑排序,小组内具有原子性进行拓扑排序。

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

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

相关文章

【机器人学】6-3.六自由度机器人运动学参数辨识- 机器人辨识参数耦合性分析

前言 上一章我们用两步优化方法求解了辨识参数&#xff0c; 【机器人学】6-2.六自由度机器人运动学参数辨识-优化方法求解辨识参数 我们给机器人的几何参数进行了数学建模&#xff0c;其中使用高斯牛顿法求解出了激光仪相对于机器人基座的坐标变换和机器人末端执行器相对于靶球…

【RTT-Studio】详细使用教程七:SGM5352外部DAC使用

文章目录 一、简介二、RTT时钟配置三、初始化配置四、完整代码五、测试验证 一、简介 本文主要介绍使用RTT-ThreadStudio来驱动SGM5352芯片的使用&#xff0c;该芯片主要是一个低功率&#xff0c;4通道&#xff0c;16位&#xff0c;电压输出DAC。它从2.7V到5.5V&#xff0c;设…

短视频矩阵系统设计:抖音短视频平台的最佳选择

随着移动互联网的快速发展&#xff0c;短视频行业异军突起&#xff0c;抖音短视频平台凭借其丰富的内容、便捷的创作工具和智能推荐算法&#xff0c;吸引了大量用户。在这个背景下&#xff0c;短视频矩阵系统应运而生&#xff0c;成为抖音短视频平台的最佳选择。本文将详细介绍…

左手坐标系、右手坐标系、坐标轴方向

一、右手坐标系 1、y轴朝上&#xff1a;webgl、Threejs、Unity、Unreal、Maya、3D Builder x&#xff1a;向右y&#xff1a;向上z&#xff1a;向前&#xff08;朝向观察者、指向屏幕外&#xff09; 2、z轴朝上&#xff1a;cesium、blender x&#xff1a;向右y&#xff1a;向前…

C# 方法的重载(Overload)

在C#中&#xff0c;方法的重载&#xff08;Overloading&#xff09;是指在一个类中可以有多个同名的方法&#xff0c;只要这些方法具有不同的方法签名&#xff08;即参数的数量、类型或顺序不同&#xff09;。这使得你可以使用相同的方法名称来执行相似但参数不同的操作&#x…

GEE必须会教程——基于Landsat影像构建NDVI时间序列

很久很久以前&#xff0c;小编写了一篇基于MODIS影像构建归一化植被指数的文章&#xff0c;不知道大家还有没有印象&#xff0c;有一段时间没有更新时间序列分析相关的文章了。 今天&#xff0c;我们来看看基于Lansat影像&#xff0c;我们来看看在GEE上如何构建NDVI的时间序列。…

AI跟踪报道第50期-新加坡内哥谈技术-本周AI新闻: 听听没有Scarlett Johansson的GPT-4o更加震撼

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

shellcode加密免杀

通过加密shellcode方式过安全软件拦截 先说结论&#xff0c;笔者没成功 shellcode&#xff1a; Shellcode 是一段用于在目标系统上执行特定操作的机器码。它通常被用于利用软件漏洞&#xff0c;以获取对目标系统的控制权或执行特定的恶意行为。 Shellcode 可以执行诸如创建进程…

MySQL 预处理、如何在 [Node.js] 中使用 MySQL?

前面文章我们已经总结了mysql下载安装配置启动以及如何用 Navicat 连接&#xff0c;还有MySQL的入门基础知识 、Node.js的基本知识、Express框架基于Node.js基础知识、下面我们总结如何在Node.js中使用MySQL数据库以及MySQL预处理基本知识。 目录 一、MySQL预处理 二、如何在…

JavaFX布局-GridPane

JavaFX布局-GridPane 常用实行alignmenthgapvgappaddinggridLinesVisible 实现方式Java实现fxml实现 使用行和列来组织其子节点将节点放置在二维网格中的任何单元格&#xff0c;同时也可以设置跨越行、跨越列 常用实行 alignment 对齐方式&#xff0c;设置内容居中&#xff0…

032-GeoGebra中级篇-列表与集合(list and set)及常用操作大全

列表在 GeoGebra 中扮演着重要角色&#xff0c;使用户能够处理和管理一组数值、点或对象。这些列表可以用于执行多种操作&#xff0c;如计算平均值、排序、查找最大或最小值、绘制点的集合等。通过使用列表&#xff0c;用户可以简化复杂的计算步骤&#xff0c;增强图形的动态性…

《计算机组成原理》(第3版)第1章 计算机系统概论 复习笔记

第1章 计算机系统概论 一、计算机系统简介 &#xff08;一&#xff09;计算机的软硬件概念 1&#xff0e;计算机系统由“硬件”和“软件”两大部分组成 &#xff08;1&#xff09;所谓“硬件”&#xff0c;是指计算机的实体部分&#xff0c;如主机、外部设备等。 &#xff0…

phpMyAdmin 漏洞

一、日志文件拿shell 在sql语句执行界面执行命令 将日志功能打开 再次查看 更改日志保存路径 擦看是否更改成功 植入一句话木马 访问木马 使用工具连接 二、导⼊导出拿WebShell 判断网站位置 判断在www在Extensions同级 写shell 访问shell,使用工具连接 三、可视化界面getshe…

C++11特性(二)

系列文章目录 C11特性(一) 文章目录 系列文章目录前言一、可变模板参数1.1 什么是可变参数模板1.2 如何打印可变模板参数的内容递归函数方式展开参数包逗号表达式展开参数包 1.3 emplace_back的实现1.4 可变模板参数为何高效 二、lambda表达式2.1 C98中的一个例子2.2 lambda表…

基于JAVA的商品供应管理系统-JavaEE

点击下载源码 基于JAVA的商品供应管理系统-JavaEE 摘 要 当今社会己进入信息社会时代&#xff0c;信息己经受到社会的广泛关注&#xff0c;被看作社会和科学技术发展的三大支柱&#xff08;材料、能源、信息&#xff09;之一。信息是管理的基础&#xff0c;是进行决策的基本依…

vue3使用svg(无废话版)

1.去阿里云矢量图标库&#xff0c;复制svg代码 2.新建一个phone.svg文件(存放在assets/icons/phone.svg)&#xff0c;内容是刚刚复制的svg代码 <svg t"1722592653925" class"icon" viewBox"0 0 1024 1024" version"1.1" xmlns&quo…

数据失踪?这四款U盘数据恢复利器教你如何避免错误操作与保障安全性

当U盘上的数据不见了&#xff0c;数据恢复软件就像是你的救命稻草。一些好的数据恢复软件还会提供安全的恢复选项&#xff0c;确保在恢复的过程中不会对原来的数据造成损害。接下来&#xff0c;我们就来看看这些顶级的U盘数据恢复软件是怎么帮我们恢复U盘数据的&#xff0c;同时…

红旗E-QM5起火,一汽红旗否认车辆质量问题

近日&#xff0c;据媒体报道&#xff0c;7月31日下午&#xff0c;长春一辆一汽红旗E-QM5发生起火事故。 一汽红旗方面则表示&#xff1a;“现场勘查和初步调查表明&#xff0c;该事件并非因车辆自身质量问题导致自燃。疑似车辆在行驶过程中与路面井盖发生碰撞导致动力电池受损&…

专业130+总分430+浙大浙江大学842考研信号系统与数字电路经验电子信息与通信工程真题,大纲,参考书。

通过接近一年的备考&#xff0c;专业842信号和数电总结130&#xff08;专业课比预期低&#xff09;&#xff0c;总分430如愿上岸浙大&#xff0c;这一路复习走过弯路&#xff0c;淋过雨&#xff0c;也走过大路&#xff0c;风和日丽&#xff0c;总结一些自己的心得&#xff0c;希…