我想知道是否可以使用Dijkstra算法在有向无环图中找到最长路径。我知道在一般的图中,由于存在负权重环,使用Dijkstra算法是无法找到最长路径的。但在DAG中应该可以使用,我认为。通过谷歌搜索我发现了很多互相矛盾的信息。有些人说它适用于DAG,而有些人则说它不适用,但我没有找到证据或反例。请问有人能给我提供一个证明或反例吗?
我想知道是否可以使用Dijkstra算法在有向无环图中找到最长路径。我知道在一般的图中,由于存在负权重环,使用Dijkstra算法是无法找到最长路径的。但在DAG中应该可以使用,我认为。通过谷歌搜索我发现了很多互相矛盾的信息。有些人说它适用于DAG,而有些人则说它不适用,但我没有找到证据或反例。请问有人能给我提供一个证明或反例吗?
我思考了这个问题,我认为一般情况下是不可能的。我认为仅仅是无环并不足够。
例如:
我们想要在这个有向无环图中从a到c。
a - > b - > c
| /\
v |
d - - - - -
d-c长度为4
a-d长度为1
其他路径的长度均为2
如果你只是将最小函数替换为最大函数,该算法会导致a-b-c成为最长路径,但实际上最长路径是a-d-c。
我发现了两种特殊情况可以使用Dijkstra算法来计算最长路径:
d
之前,你先访问了c
,即a-b-c
与a-d-c
的区别。 - StyleZ最长距离问题在任何图形中都没有最优子结构,无论是DAG还是非DAG。然而,在图形G上的任何最长距离问题都等同于在转换后的图形G'中寻找最短距离问题,即每个边权重的符号被反转。
如果预计转换后的图形G'具有负边和循环,则使用Bellman-Ford算法查找最短距离。然而,如果保证G仅具有非负权重(即G'具有非正权重),则Dijkstra算法可能比Bellman-Ford更好的选择。(请参见“Evgeny Kluev”关于单源最长路径的图形-Dijkstra的回答)如果G是DAG,则G'也将是DAG。对于DAG,我们有更好的算法来查找最短距离,并且应该选择它而不是Dijkstra或Bellman-Ford。
摘要:
最长路径问题没有最优子结构,因此仅将Dijkstra算法中的最小权重函数修改为最大权重函数并不能适用于图形,无论是有向无环图还是不是。我们不是以一种琐碎的方式修改任何最短路径算法,而是转换G,并查看哪个最短路径算法在转换后的G上效果最好。
注意:
A-------B
| | assume: edges A-B, B-C, C-A of same weight
| |
+-------C
MAX_DIS(A,B) = MAX_DIS(A,C) + MAX_DIS(C,B) should be satisfied.
但事实并非我们所看到的,MAX_DIS(A,C)= A->B->C,MAX_DIS(C,B)= C->A->B,因此它提供了一个例子,表明最长距离问题可能没有最优子结构。
using System;
using System.Collections.Generic;
using System.Linq;
namespace Longest_Path
{
public static class Program
{
public static void Main(string[] args)
{
var nodesCount = int.Parse(Console.ReadLine());
var edgesCount = int.Parse(Console.ReadLine());
var graph = new List<Edge>[nodesCount + 1];
var comesFrom = new int[nodesCount + 1];
var nodesTime = new double[nodesCount + 1];
for (int i = 0; i <= nodesCount; i++)
{
comesFrom[i] = -1;
nodesTime[i] = double.PositiveInfinity;
}
for (int i = 0; i < edgesCount; i++)
{
var input = Console.ReadLine().Split();
var from = int.Parse(input[0]);
var to = int.Parse(input[1]);
var weight = double.Parse(input[2]);
var edge = new Edge(from, to, weight * -1);
if (graph[from] == null)
{
graph[from] = new List<Edge>();
}
if (graph[to] == null)
{
graph[to] = new List<Edge>();
}
graph[from].Add(edge);
}
var source = int.Parse(Console.ReadLine());
var destination = int.Parse(Console.ReadLine());
nodesTime[source] = 0;
var priorityQueue = new Queue<int>();
priorityQueue.Enqueue(source);
while (priorityQueue.Any())
{
var fastestNode = priorityQueue.Dequeue();
foreach (var child in graph[fastestNode])
{
if (!priorityQueue.Contains(child.to))
{
priorityQueue.Enqueue(child.to);
}
var currentTime = nodesTime[child.from] + child.weight;
if (currentTime < nodesTime[child.to])
{
nodesTime[child.to] = currentTime;
comesFrom[child.to] = child.from;
}
}
priorityQueue = new Queue<int>(priorityQueue.OrderBy(x => nodesTime[x]));
}
Console.WriteLine(nodesTime[destination] * -1);
var path = new Stack<int>();
path.Push(destination);
var currentNode = destination;
while (comesFrom[currentNode] != -1)
{
currentNode = comesFrom[currentNode];
path.Push(currentNode);
}
Console.WriteLine(string.Join(' ', path));
}
}
public class Edge
{
public readonly int from;
public readonly int to;
public readonly double weight;
public Edge(int firstNode, int secondNode, double weight)
{
this.from = firstNode;
this.to = secondNode;
this.weight = weight;
}
}
}
我建议您修改Dijkstra算法,以考虑边权值的倒数。因为图是无环的,算法不会进入无限循环,并使用负权重来保持优化。此外,现在正的权值变成了负的,但是同样也没有环。即使图是无向的,只要禁止重新插入已访问的节点(即停止两个节点之间的无限跳动,因为添加负值始终会更好),这也将起作用。
声望不足,无法评论@punkyduck的答案,但我想提到在DAG中用max
替换min
a ——2——→ b ——2——→ c
│ ↑
│ │
1 4
│ │
└——————→ d ———————┘
实际上是有效的,因为算法将
a
⇨ ab=2, ad=1
⇨ l(b)=(ab,2), l(d)=(ad,1)
b
,因为我们使用了 max
⇨ abc=2
⇨ l(c)=(abc,2)
c
,因为 abc>ad
⇨ 什么也没发生d
⇨ adc=5
⇨ 更新 l(c)=(adc,5)
因此,在最后一步找到了正确的最长路径 adc
。只是指出错误。