LeetCode 1857. Largest Color Value in a Directed Graph【拓扑排序,动态规划】困难

news/2024/4/28 18:27:48/文章来源:https://blog.csdn.net/myRealization/article/details/131996938

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

给你一个 有向图 ,它含有 n 个节点和 m 条边。节点编号从 0 到 n - 1 。

给你一个字符串 colors ,其中 colors[i] 是小写英文字母,表示图中第 i 个节点的 颜色 (下标从 0 开始)。同时给你一个二维数组 edges ,其中 edges[j] = [aj, bj] 表示从节点 aj 到节点 bj 有一条 有向边 。

图中一条有效 路径 是一个点序列 x1 -> x2 -> x3 -> ... -> xk ,对于所有 1 <= i < k ,从 xi 到 xi+1 在图中有一条有向边。路径的 颜色值 是路径中 出现次数最多 颜色的节点数目。

请你返回给定图中有效路径里面的 最大颜色值  如果图中含有环,请返回 -1 。

示例 1:

输入:colors = "abaca", edges = [[0,1],[0,2],[2,3],[3,4]]
输出:3
解释:路径 0 -> 2 -> 3 -> 4 含有 3 个颜色为 "a" 的节点(上图中的红色节点)。

示例 2:

输入:colors = "a", edges = [[0,0]]
输出:-1
解释:从 00 有一个环。

提示:

  • n == colors.length
  • m == edges.length
  • 1 <= n <= 10^5
  • 0 <= m <= 10^5
  • colors 只含有小写英文字母。
  • 0 <= aj, bj < n

解法 拓扑排序 + 动态规划

我们需要求出的答案等价于:对于一种颜色 c c c ,以及一条路径 path \textit{path} path ,其中颜色为 c c c 的节点有 path c \textit{path}_c pathc。我们希望挑选 c c c 以及 p a t h path path ,使得 path c \textit{path}_c pathc 的值最大。

我们可以枚举颜色 c c c ,随后选出可以使得 path c \textit{path}_c pathc 达到最大值的 path \textit{path} path 。这些 path c \textit{path}_c pathc 中的最大值即为答案。

  • 如果给定的有向图包含环,那么它不存在拓扑排序。
  • 如果给定的有向图不包含环,那么这个有向图是一个「有向无环图」,它一定存在拓扑排序。
    根据拓扑排序的性质,如果节点 a a a 有一条有向边指向节点 b b b ,那么 b b b 在拓扑排序中一定出现在 a a a 之后。因此,一条有效路径上点的顺序与它们在拓扑排序中出现的顺序是一致的

我们可以根据拓扑排序来进行动态规划。设 d p [ v ] [ c ] dp[v][c] dp[v][c] 表示以节点 v v v 为终点的所有有效路径中,包含颜色 c c c 的节点数量的最大值。在进行状态转移时,我们考虑所有 v v v 的前驱节点(即有一条有向边指向 v v v 的节点) prev v \textit{prev}_v prevv
d p [ v ] [ c ] = ( max ⁡ u ∈ prev j d p [ u ] [ c ] ) + I ( v , c ) dp[v] [c] = \left( \max_{u \in \textit{prev}_j} dp [u][c] \right) + \mathbb{I}(v, c) dp[v][c]=(uprevjmaxdp[u][c])+I(v,c)
即找出前驱节点中包含颜色 c c c 的节点数量最多的那个节点进行转移,并且如果 v v v 本身的颜色为 c c c d p [ v ] [ c ] dp[v][c] dp[v][c] 的值就增加 1 1 1 。这里 I ( v , c ) \mathbb{I}(v, c) I(v,c) 为示性函数,当节点 v v v 的颜色为 c c c 时,函数值为 1 1 1 ,否则为 0 0 0 。那么 path c \textit{path}_c pathc 的最大值即为 d p [ v ] [ c ] dp[v][ c] dp[v][c] 中的最大值。

我们可以将状态转移,融入使用广度优先搜索的方法求解拓扑排序的过程中。当遍历到节点 u u u 时:

  • 如果 u u u 的颜色为 c c c ,那么将 d p [ u ] [ c ] dp[u] [c] dp[u][c] 的值增加 1 1 1
  • 枚举 u u u 的所有后继节点(即从 u u u 出发经过一条有向边可以到达的节点),对于后继节点 v v v ,将 d p [ v ] [ c ] dp[v][c] dp[v][c] 更新为其与 d p [ u ] [ c ] dp[u][c] dp[u][c] 的较大值。

这样的操作与上文描述的状态转移方程是一致的。它的好处在于,如果使用广度优先搜索的方法求解拓扑排序,那么我们需要使用邻接表存储所有的有向边,而上文的动态规划是通过「枚举 v → v → v 枚举前驱节点 u u u 」进行状态转移的,这就需要我们额外存储所有边的反向边,才能通过 v v v 找到所有的前驱节点。如果我们通过「枚举 u → u \to u 枚举后继节点 v v v 」进行状态转移,这样就与拓扑排序存储的边保持一致了。

class Solution {
public:int largestPathValue(string colors, vector<vector<int>>& edges) {int n = colors.size();vector<int> g[n];vector<int> ind(n); // 节点的入度统计,用于找出拓扑排序中最开始的节点for (vector<int>& e : edges) {g[e[0]].push_back(e[1]);++ind[e[1]];}vector<array<int, 26>> dp(n);int found = 0, ans = 0;queue<int> q;for (int i = 0; i < n; ++i) if (ind[i] == 0) q.push(i); while (!q.empty()) {int u = q.front(); q.pop();++found;++dp[u][colors[u] - 'a']; // 节点u对应的颜色+1ans = max(ans, dp[u][colors[u] - 'a']);for (int v : g[u]) {for (int c = 0; c < 26; ++c)dp[v][c] = max(dp[u][c], dp[v][c]);if (--ind[v] == 0) q.push(v);} } return found != n ? -1 : ans;}
};

复杂度分析:

  • 时间复杂度: O ( n + m ∣ Σ ∣ ) O(n+m |\Sigma |) O(n+m∣Σ∣),其中 ∣ Σ ∣ |\Sigma | ∣Σ∣ 表示颜色的数量,在本题中 ∣ Σ ∣ = 26 |\Sigma |=26 ∣Σ∣=26
    一般的拓扑排序需要的时间为 O ( n + m ) O(n+m) O(n+m)。而在本题中在拓扑排序的过程中加入了状态转移,由于一条有向边对应着 ∣ Σ ∣ |\Sigma | ∣Σ∣ 次状态转移,因此拓扑排序的时间复杂度实际为 O ( n + m ∣ Σ ∣ ) O(n + m|\Sigma|) O(n+m∣Σ∣)
  • 空间复杂度: O ( n ∣ Σ ∣ + m ) O(n|\Sigma| + m) O(n∣Σ∣+m) 。我们需要 O ( n ∣ Σ ∣ ) O(n |\Sigma|) O(n∣Σ∣) 的空间存储对应数量的状态;我们需要 O ( n + m ) O(n+m) O(n+m) 的空间存储邻接表;我们需要 O ( n ) O(n) O(n) 的队列空间进行拓扑排序。将它们相加,即可得到总时间复杂度为 O ( n ∣ Σ ∣ + m ) O(n |\Sigma| + m) O(n∣Σ∣+m)

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【UE5】快速认识入门

目录 &#x1f31f;1. 快速安装&#x1f31f;2. 简单快捷键操作&#x1f31f;3. 切换默认的打开场景&#x1f31f;4. 虚幻引擎术语 &#x1f31f;1. 快速安装 进入Unreal Engine 5官网进行下载即可&#xff1a;UE5 &#x1f4dd;官方帮助文档 打开后在启动器里创建5.2.1引擎…

冯诺依曼体系的认识、来源、原理、组成、功能和特点

目录 一.认识冯诺依曼 二.冯诺依曼体系结构的来源 三.冯诺依曼体系结构计算机 3.1工作原理 3.2组成部件 3.3功能和特点 &#x1f381;个人主页&#xff1a;tq02的博客_CSDN博客-C语言,Java,Java数据结构领域博主 &#x1f3a5; 本文由 tq02 原创&#xff0c;首发于 CSDN&…

C++笔记之vector的resize()和clear()用法

C笔记之vector的resize()和clear()用法 code review! 文章目录 C笔记之vector的resize()和clear()用法1.resize()2.clear() 1.resize() 运行 2.clear() 运行

Jsp+Ssh+Mysql实现的简单的企业物资信息管理系统项目源码附带视频指导运行教程

由jspssh&#xff08;springstruts2mysql&#xff09;实现的企业物资信息管理系统&#xff0c;系统功能比较简单&#xff0c;实现了基本的管理员、操作员等用户管理、物品分类管理、物品管理、入库管理、出库管理、库存预警、客户管理、供应商管理等基本功能需要的可以联系我分…

怎么在线制作证件?教你一键生成证件照

无论是申请身份证、护照、驾照还是学生证&#xff0c;都需要一张清晰、规范的证件照。但是&#xff0c;为了拍摄一张完美的证件照&#xff0c;需要付出不少时间和精力。而现在&#xff0c;我们可以使用压缩图网站提供的证件照制作工具&#xff0c;轻松制作出一张清晰、规范的证…

《重构的时机和方法》——让你的代码更健壮、更易维护

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小黄&#xff0c;独角兽企业的Java开发工程师&#xff0c;CSDN博客专家&#xff0c;阿里云专家博主&#x1f4d5;系列专栏&#xff1a;Java设计模式、Spring源码系列、Netty源码系列、Kafka源码系列、JUC源码…

FFmpeg aresample_swr_opts的解析

ffmpeg option的解析 aresample_swr_opts是AVFilterGraph中的option。 static const AVOption filtergraph_options[] {{ "thread_type", "Allowed thread types", OFFSET(thread_type), AV_OPT_TYPE_FLAGS,{ .i64 AVFILTER_THREAD_SLICE }, 0, INT_MA…

【学会动态规划】打家劫舍 II(12)

目录 动态规划怎么学&#xff1f; 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后&#xff1a; 动态规划怎么学&#xff1f; 学习一个算法没有捷径&#xff0c;更何况是学习动态规划&#xff0c; 跟我…

P3818 小A和uim之大逃离 II

题目 思路 一眼bfs 好像需要记录的东西有点多啊&#xff0c;那就交给数组吧 s t i j 0 / 1 st_{ij0/1} stij0/1​表示用/没用特殊步走到(i,j)的步数&#xff0c;然后套bfs模板即可 代码 #include<bits/stdc.h> using namespace std; const int N1005; int n,m,d,r,st…

使用Kmeans算法完成聚类任务

聚类任务 聚类任务是一种无监督学习任务&#xff0c;其目的是将一组数据点划分成若干个类别或簇&#xff0c;使得同一个簇内的数据点之间的相似度尽可能高&#xff0c;而不同簇之间的相似度尽可能低。聚类算法可以帮助我们发现数据中的内在结构和模式&#xff0c;发现异常点和离…

Pycharm debug程序,跳转至指定循环条件/循环次数

在断点出右键&#xff0c;然后设置条件 示例 for i in range(1,100):a i 1b i 2print(a, b, i) 注意&#xff1a; 1、你应该debug断点在循环后的位置而不是循环上的位置&#xff0c;然后你就可以设置你的条件进入到指定的循环上了 2、设置条件&#xff0c;要使用等于符号…

系统集成|第七章(笔记)

目录 第七章 范围管理7.1 项目范围管理概念7.2 主要过程7.2.1 规划范围管理7.2.2 收集需求7.2.3 定义范围7.2.4 创建工作分解结构 - WBS7.2.5 范围确认7.2.6 范围控制 上篇&#xff1a;第六章、整体管理 第七章 范围管理 7.1 项目范围管理概念 概述&#xff1a;项目范围管理就…

【深度学习Week3】ResNet+ResNeXt

ResNetResNeXt 一、ResNetⅠ.视频学习Ⅱ.论文阅读 二、ResNeXtⅠ.视频学习Ⅱ.论文阅读 三、猫狗大战Lenet网络Resnet网络 四、思考题 一、ResNet Ⅰ.视频学习 ResNet在2015年由微软实验室提出&#xff0c;该网络的亮点&#xff1a; 1.超深的网络结构&#xff08;突破1000层&…

C#之泛型

目录 一、概述 二、C#中的泛型 继续栈的示例 三、泛型类 &#xff08;一&#xff09;声明泛型类 &#xff08;二&#xff09;创建构造类型 &#xff08;三&#xff09;创建变量和实例 &#xff08;四&#xff09;比较泛型和非泛型栈 四、类型参数的约束 &#xff08;一…

golangd\pycharm-ai免费代码助手安装使用gpt4-免费使用--[推荐]

golangd-ai免费代码助手安装使用,pycharm可以使用&#xff0c;估计只要是xx的ide都是可以使用这个插件 目前GPT4以及gpt的大规模使用&#xff0c;如何快速掌握以及在ide中快速使用的办法&#xff0c;今天安装一款golangd编辑器的插件已经使用 一、安装以及使用 1.在golangd中…

骨传导耳机是什么?为什么不用塞到耳朵里?

骨传导耳机其实就跟它的名字一样&#xff0c;用骨传导声音的耳机&#xff0c;整个声音传导过程都是开放双耳的&#xff0c;不接触耳膜&#xff0c;佩戴非常舒适的耳机。 为什么不需要塞进耳朵里&#xff0c;首先咱们要先知道骨传导的原理&#xff1a; 如上图所示&#xff0c;骨…

Linux环境搭建(XShell+云服务器)

好久不见啊&#xff0c;放假也有一周左右了&#xff0c;简单休息了下&#xff08;就是玩了几天~~&#xff09;&#xff0c;最近也是在学习Linux&#xff0c;现在正在初步的学习阶段&#xff0c;本篇将会简单的介绍一下Linux操作系统和介绍Linux环境的安装与配置&#xff0c;来帮…

七、用户画像

目录 7.1 什么是用户画像7.2 标签系统7.2.1 标签分类方式7.2.2 多渠道获取标签 7.3 用户画像数据特征7.3.1 常见的数据形式7.3.2 文本挖掘算法7.3.3 嵌入式表示7.3.4 相似度计算方法 7.4 用户画像应用 因此只基于某个层面的数据便可以产生部分个体面像&#xff0c;可用于从特定…

软件测试/测试开发丨Selenium环境安装与使用

Selenium 官方网站&#xff1a; www.selenium.dev/ 简介&#xff1a; 用于web浏览器测试的工具&#xff1b;支持的浏览器包括IE&#xff0c;Firefox&#xff0c;Safari&#xff0c;Chrome&#xff0c;Edge等&#xff1b;使用简单&#xff0c;可使用Java&#xff0c;Python等…

派森编程软件python好学吗,派森语言python干什么的

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;派森编程软件python有什么用&#xff0c;派森编程软件python好学吗&#xff0c;现在让我们一起来看看吧&#xff01; 1、python真的值得学吗&#xff1f; 不建议学python的原因&#xff1a; 1、语言性能差 对于C老手…