1857.有向图中最大颜色值:拓扑排序+动态规划

【LetMeFly】1857.有向图中最大颜色值:拓扑排序+动态规划

力扣题目链接:https://leetcode.cn/problems/largest-color-value-in-a-directed-graph/

给你一个 有向图 ,它含有 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
解释:从 0 到 0 有一个环。

 

提示:

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

解题方法:拓扑排序+动态规划

记录每个节点的入度(入度为指向这个节点的边的个数),记录每个节点指向哪些节点。

创建一个队列,一旦有节点入度为0则入队(拓扑排序)。

创建一个DP数组,DP[i][j]表示从路径起点到节点i颜色j的最大个数。

队列非空时:

每次从队列中取出一个节点(thisNode),DP数组中这个节点自身的颜色值加一。

遍历这个节点指向的所有节点(nextNode),被指向节点入度减一,更新被指向节点的每种颜色最大值dp[nextNode][j]为max(dp[nextNode][j], dp[thisNode][j])。

最终,若仍有节点入度非零,则代表有环,返回-1;否则,返回dp[i][j]中最大的一个。

  • 时间复杂度$O((m+n)C)$,其中$C=26$
  • 空间复杂度$O(nC+m)$

AC代码

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
* @Author: LetMeFly
* @Date: 2025-05-26 10:24:14
* @LastEditors: LetMeFly.xyz
* @LastEditTime: 2025-05-26 23:26:40
*/
class Solution {
public:
int largestPathValue(string colors, vector<vector<int>>& edges) {
vector<vector<int>> out(colors.size());
vector<int> indegree(colors.size());
for (vector<int>& edge: edges) {
out[edge[0]].push_back(edge[1]);
indegree[edge[1]]++;
}
queue<int> q;
for (int i = 0; i < colors.size(); i++) {
if (!indegree[i]) {
q.push(i);
}
}
vector<array<int, 26>> dp(colors.size());
int ans = 0;
while (q.size()) {
int thisNode = q.front();
q.pop();
ans = max(ans, ++dp[thisNode][colors[thisNode] - 'a']);
for (int nextNode : out[thisNode]) {
indegree[nextNode]--;
if (!indegree[nextNode]) {
q.push(nextNode);
}
for (int i = 0; i < 26; i++) {
dp[nextNode][i] = max(dp[nextNode][i], dp[thisNode][i]);
}
}
}
for (int i = 0; i < colors.size(); i++) {
if (indegree[i]) {
return -1;
}
}
return ans;
}
};

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
'''
Author: LetMeFly
Date: 2025-05-26 22:02:44
LastEditors: LetMeFly.xyz
LastEditTime: 2025-05-26 23:34:26
'''
from typing import List

class Solution:
def largestPathValue(self, colors: str, edges: List[List[int]]) -> int:
out = [[] for _ in range(len(colors))]
indegree = [0] * len(colors)
for x, y in edges:
out[x].append(y)
indegree[y] += 1
q = []
for i in range(len(colors)):
if indegree[i] == 0:
q.append(i)
dp = [[0] * 26 for _ in range(len(colors))]
ans = 0
while q:
thisNode = q.pop()
dp[thisNode][ord(colors[thisNode]) - ord('a')] += 1
ans = max(ans, dp[thisNode][ord(colors[thisNode]) - ord('a')])
for nextNode in out[thisNode]:
indegree[nextNode] -= 1
if not indegree[nextNode]:
q.append(nextNode)
for i in range(26):
dp[nextNode][i] = max(dp[nextNode][i], dp[thisNode][i])
if any(indegree):
return -1
return ans

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*
* @Author: LetMeFly
* @Date: 2025-05-26 22:02:44
* @LastEditors: LetMeFly.xyz
* @LastEditTime: 2025-05-27 00:02:40
*/
import java.util.List;
import java.util.ArrayList;
import java.util.Queue;
import java.util.LinkedList;

class Solution {
public int largestPathValue(String colors, int[][] edges) {
List<Integer>[] out = new ArrayList[colors.length()];
for (int i = 0; i < colors.length(); i++) {
out[i] = new ArrayList<>();
}
int[] indegree = new int[colors.length()];
for (int[] edge : edges) {
out[edge[0]].add(edge[1]);
indegree[edge[1]]++;
}
Queue<Integer> q = new LinkedList<>();
for (int i = 0; i < colors.length(); i++) {
if (indegree[i] == 0) {
q.offer(i);
}
}
int[][]dp = new int[colors.length()][26];
int ans = 0;
while (!q.isEmpty()) {
int thisNode = q.poll();
ans = Math.max(ans, ++dp[thisNode][colors.charAt(thisNode) - 'a']);
for (int nextNode : out[thisNode]) {
indegree[nextNode]--;
if (indegree[nextNode] == 0) {
q.offer(nextNode);
}
for (int i = 0; i < 26; i++) {
dp[nextNode][i] = Math.max(dp[nextNode][i], dp[thisNode][i]);
}
}
}
for (int i = 0; i < colors.length(); i++) {
if (indegree[i] != 0) {
return -1;
}
}
return ans;
}
}

Go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*
* @Author: LetMeFly
* @Date: 2025-05-26 22:02:44
* @LastEditors: LetMeFly.xyz
* @LastEditTime: 2025-05-26 23:59:12
*/
package main

func largestPathValue(colors string, edges [][]int) (ans int) {
out := make([][]int, len(colors))
for i := range out {
out[i] = make([]int, 0)
}
indegree := make([]int, len(colors))
for _, edge := range edges {
out[edge[0]] = append(out[edge[0]], edge[1])
indegree[edge[1]]++
}
q := []int{}
for i, v := range indegree {
if v == 0 {
q = append(q, i)
}
}
dp := make([][]int, len(colors))
for i := range dp {
dp[i] = make([]int, 26)
}
for len(q) > 0 {
thisNode := q[len(q) - 1]
q = q[:len(q) - 1]
dp[thisNode][colors[thisNode] - 'a']++
ans = max(ans, dp[thisNode][colors[thisNode] - 'a'])
for _, nextNode := range out[thisNode] {
indegree[nextNode]--
if indegree[nextNode] == 0 {
q = append(q, nextNode)
}
for i := 0; i < 26; i++ {
dp[nextNode][i] = max(dp[nextNode][i], dp[thisNode][i])
}
}
}
for _, v := range indegree {
if v != 0 {
return -1
}
}
return
}

同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~

千篇源码题解已开源


1857.有向图中最大颜色值:拓扑排序+动态规划
https://blog.letmefly.xyz/2025/05/27/LeetCode 1857.有向图中最大颜色值/
作者
发布于
2025年5月27日
许可协议