有向无环图的最长路径【Java实现】

题目描述

现有一个共n个顶点、m条边的有向无环图(假设顶点编号为从0n-1),求图的所有路径中边权之和的最大值(不固定起点和终点)

输入描述

第一行两个整数n、m(1≤n≤100,0≤m≤n(n−1)),分别表示顶点数、边数;

接下来m行,每行三个整数u、v、w(0≤u≤n−1,0≤v≤n−1,u≠v,1≤w≤100),表示一条边的起点和终点的编号及边权。数据保证不会有重边

输出描述

输出一个整数,表示路径上边权之和的最大值

样例

输入

6 7
0 1 1
0 2 2
1 3 3
1 4 4
2 4 2
3 5 2
4 5 3

输出

8
image-20230312104401240

思路分析

  • 题目不限制起点终点,因此根据贪心的原则,有路径就应该试着走一走,同时应该把每个顶点都当作起点试一遍
  • 如果我们能够知道由当前顶点i出发的最大权值之和,再通过比较各个权值之和选出最大值maxVal,就可以作为我们的答案。因此问题关键在于:如何记录由当前顶点i出发的最大权值之和?由此我们可以考虑使用动态规划
  • 设置g[n][n]用来存放顶点和边的关系,设置dp数组,其中dp[i]代表由当前顶点i出发所能得到的最大权值之和
  • 假设当前顶点i到顶点j之间有路径,那么由dp[i]的取值无非只有两种:
    1. dp[i]本身,即当前知道的i出发能够得到的权值之和,但是由于我们对dp的处理还不完全,因此此时的权值之和不一定是最大的
    2. dp[j] + g[i][j],即顶点i到顶点j的权重加上从j出发能够得到的权值之和
    3. 选择其中最大的作为dp[i]真正的值【此时i是固定的,j是变化的】
  • 此处可以做一个优化,即当dp[i]>0时直接返回dp[i],因为此时说明dp[i]已经被处理过,内部存储的就是从i点出发的最大值,不需要重复处理
  • 最后可以得到整体被处理好的数组dp,其中的最大值就是我们的答案

代码实现

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();
		int m = scanner.nextInt();
		// 存放地图
		int g[][] = new int[n][n];
		// dp[i]代表从 i 出发能够得到的最大权重之和
		int dp[] = new int[n];
		// 对图进行初始化
		for (int i = 0; i < m; i++) {
			int u = scanner.nextInt();
			int v = scanner.nextInt();
			int w = scanner.nextInt();
			g[u][v] = w;
		}
		int maxVal = 0;
		for (int i = 0; i < n; i++) {
			maxVal = Math.max(maxVal, getDAGMaxLength(g, dp, i, n));
		}
		System.out.println(maxVal);

	}
	
	// 返回从 i 出发的有向无环图最大权值之和路径
	public static int getDAGMaxLength(int g[][], int dp[], int i, int n) {
		// 说明 i 点已经处理过,不需要重复计算
		if (dp[i] > 0) {
			return dp[i];
		}
		for (int j = 0; j < n; j++) {
			// i 到 j 有路可走
			if (g[i][j] > 0) {
				// 看看从 j 出发的最大权值之和 + 当前顶点 i 到 j 的权重是否大于直接从 i 出发的最大权值之和
				dp[i] = Math.max(dp[i], getDAGMaxLength(g, dp, j, n) + g[i][j]);
			}
		}
		return dp[i];
	}

}
求一个带权有向图的最长路径,可以使用拓扑排序和动态规划算法。 具体步骤如下: 1. 对带权有向图进行拓扑排序,得到一个拓扑序列。 2. 初始化一个数组d,d[i]表示从拓扑序列中第i个节点到其它节点的最长路径长度,初始值为负无穷。 3. 对于拓扑序列中的每个节点i,遍历其所有的出边,计算d[j] = max(d[j], d[i] + weight(i,j)),其中weight(i,j)表示从节点i到节点j的边的权值。 4. 最终得到的数组d中,d[i]表示从拓扑序列中第i个节点到其它节点的最长路径长度。 Java代码如下: ``` java import java.util.*; public class LongestPath { static int INF = Integer.MIN_VALUE; static class Edge { int to, weight; public Edge(int to, int weight) { this.to = to; this.weight = weight; } } static int[] topoSort(List<List<Edge>> graph) { int n = graph.size(); int[] inDegree = new int[n]; for (int i = 0; i < n; i++) { for (Edge e : graph.get(i)) { inDegree[e.to]++; } } Queue<Integer> queue = new LinkedList<>(); for (int i = 0; i < n; i++) { if (inDegree[i] == 0) { queue.offer(i); } } int[] order = new int[n]; int i = 0; while (!queue.isEmpty()) { int u = queue.poll(); order[i++] = u; for (Edge e : graph.get(u)) { if (--inDegree[e.to] == 0) { queue.offer(e.to); } } } return order; } static int[] longestPath(List<List<Edge>> graph) { int n = graph.size(); int[] order = topoSort(graph); int[] d = new int[n]; Arrays.fill(d, INF); d[order[0]] = 0; for (int i = 0; i < n; i++) { int u = order[i]; if (d[u] == INF) continue; for (Edge e : graph.get(u)) { int v = e.to; int w = e.weight; d[v] = Math.max(d[v], d[u] + w); } } return d; } public static void main(String[] args) { int n = 6; List<List<Edge>> graph = new ArrayList<>(); for (int i = 0; i < n; i++) { graph.add(new ArrayList<>()); } graph.get(0).add(new Edge(1, 2)); graph.get(0).add(new Edge(2, 3)); graph.get(1).add(new Edge(2, 1)); graph.get(1).add(new Edge(3, 4)); graph.get(2).add(new Edge(4, 5)); graph.get(3).add(new Edge(4, 1)); graph.get(3).add(new Edge(5, 6)); int[] d = longestPath(graph); System.out.println(Arrays.toString(d)); } } ``` 以上代码输出结果为:`[0, 2, 3, 7, 8, 14]`,表示从节点0到其它节点的最长路径长度分别为0, 2, 3, 7, 8和14。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值