六、给定有向图 G = ( V , E ) G=(V,E) G=(V,E),每条边 ( u , v ) ∈ E (u,v)∈E (u,v)∈E 有一个关联值 r ( u , v ) r(u,v) r(u,v) ,该关联值是一个实数,其范围为 0 ⩽ r ( u , v ) ⩽ 1 0⩽r(u,v)⩽1 0⩽r(u,v)⩽1 ,其代表的意思是从结点 u u u到结点 v v v之间的通信链路的可靠性。可以认为, r ( u , v ) r(u,v) r(u,v) 代表的是从结点 u u u 到结点 v v v 的通信链路不失效的概率,并且假设这些概率之间相互独立。请给出一个有效的算法来找到任意两个结点之间最可靠的通信链路。如果要写代码,请用go语言。
文心一言:
要在有向图 G = ( V , E ) G=(V,E) G=(V,E) 中找到任意两个结点之间最可靠的通信链路,可以使用 Floyd-Warshall 算法进行求解。Floyd-Warshall 算法是一个经典的动态规划算法,用于计算所有结点对之间的最短路径(在这里,我们将其改造成计算所有结点对之间的最大可靠性路径)。
下面是一个使用 Go 语言实现的算法,其中 graph
表示有向图,reliability
是一个二维数组,用于存储任意两个结点之间的最大可靠性。
go
package main
import (
"fmt"
"math"
)
// Edge represents an edge in the graph with a reliability value
type Edge struct {
to int
rel float64
}
// Graph represents a directed graph
type Graph struct {
vertices int
edges map[int][]Edge
}
// NewGraph creates a new graph with a given number of vertices
func NewGraph(vertices int) *Graph {
return &Graph{
vertices: vertices,
edges: make(map[int][]Edge),
}
}
// AddEdge adds an edge to the graph with a given reliability
func (g *Graph) AddEdge(from, to int, rel float64) {
if rel < 0 || rel > 1 {
panic("Reliability must be in the range [0, 1]")
}
g.edges[from] = append(g.edges[from], Edge{to, rel})
}
// FloydWarshall computes the most reliable path between all pairs of vertices
func (g *Graph) FloydWarshall() [][]float64 {
// Initialize reliability matrix with zero values
reliability := make([][]float64, g.vertices)
for i := range reliability {
reliability[i] = make([]float64, g.vertices)
for j := range reliability[i] {
reliability[i][j] = 0.0
}
}
// Set direct edges' reliability
for from, edges := range g.edges {
for _, edge := range edges {
reliability[from][edge.to] = edge.rel
}
}
// Set self-loops to 1 (max reliability for direct path to self)
for i := 0; i < g.vertices; i++ {
reliability[i][i] = 1.0
}
// Floyd-Warshall algorithm
for k := 0; k < g.vertices; k++ {
for i := 0; i < g.vertices; i++ {
for j := 0; j < g.vertices; j++ {
if reliability[i][k]*reliability[k][j] > reliability[i][j] {
reliability[i][j] = reliability[i][k] * reliability[k][j]
}
}
}
}
return reliability
}
func main() {
// Example usage
graph := NewGraph(5)
graph.AddEdge(0, 1, 0.8)
graph.AddEdge(0, 2, 0.9)
graph.AddEdge(1, 2, 0.7)
graph.AddEdge(1, 3, 0.6)
graph.AddEdge(2, 3, 0.5)
graph.AddEdge(3, 4, 0.4)
graph.AddEdge(2, 4, 0.2)
reliability := graph.FloydWarshall()
// Print the reliability matrix
for i := 0; i < graph.vertices; i++ {
for j := 0; j < graph.vertices; j++ {
if math.IsNaN(reliability[i][j]) {
fmt.Printf("INF ") // If there's no path, we could print INF or some other indicator
} else {
fmt.Printf("%.2f ", reliability[i][j])
}
}
fmt.Println()
}
}
解释
-
Graph 数据结构:
Graph
结构体包含顶点数量和边的映射。AddEdge
方法用于添加边,并验证可靠性是否在 [0, 1] 范围内。
-
Floyd-Warshall 算法:
- 初始化可靠性矩阵
reliability
,将所有值设为 0。 - 设置直接边的可靠性值。
- 设置自环的可靠性为 1(即每个顶点到自身的可靠性最高)。
- 使用三重循环更新可靠性矩阵,找到所有结点对之间的最大可靠性路径。
- 初始化可靠性矩阵
-
主函数:
- 创建一个示例图并添加一些边。
- 调用
FloydWarshall
方法计算可靠性矩阵。 - 打印可靠性矩阵。
注意
- 如果没有路径,可靠性值会被初始化为 0,但你也可以选择使用
math.NaN()
或其他方式来表示不可达路径。 - 代码假设输入是有效的,即没有重复的边和顶点索引在有效范围内。
这个算法的时间复杂度是 O ( V 3 ) O(V^3) O(V3),其中 V V V 是顶点的数量,适用于顶点数量不太大的情况。
讯飞星火:
为了找到任意两个结点之间最可靠的通信链路,我们可以使用Floyd-Warshall算法。这个算法可以在有向图中找到所有结点对之间的最短路径,同时考虑边的权重(在这里是可靠性)。
以下是用Go语言实现的Floyd-Warshall算法:
go
package main
import (
"fmt"
)
func floydWarshall(graph [][]float64, n int) [][]float64 {
// 初始化距离矩阵
dist := make([][]float64, n)
for i := range dist {
dist[i] = make([]float64, n)
for j := range dist[i] {
if i == j {
dist[i][j] = 1.0 // 从节点到自身的距离为1
} else if graph[i][j] != 0 {
dist[i][j] = graph[i][j] // 如果有边,则直接使用边的可靠性作为距离
} else {
dist[i][j] = 0.0 // 如果没有边,则距离为0
}
}
}
// 更新距离矩阵
for k := 0; k < n; k++ {
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
if dist[i][k]*dist[k][j] > dist[i][j] {
dist[i][j] = dist[i][k] * dist[k][j]
}
}
}
}
return dist
}
func main() {
// 示例有向图,其中graph[i][j]表示从节点i到节点j的可靠性
graph := [][]float64{
{0, 0.9, 0, 0},
{0, 0, 0.8, 0},
{0, 0, 0, 0.7},
{0, 0, 0, 0},
}
n := len(graph)
dist := floydWarshall(graph, n)
// 输出结果
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
fmt.Printf("%.2f ", dist[i][j])
}
fmt.Println()
}
}
在这个代码中,我们首先初始化一个距离矩阵dist
,然后使用Floyd-Warshall算法更新这个矩阵。最后,我们输出更新后的距离矩阵,其中dist[i][j]
表示从节点i
到节点j
的最可靠通信链路的可靠性。
360智脑:
为了找到任意两个结点之间最可靠的通信链路,我们可以使用动态规划或者图的最短路径算法的一种变体。由于我们是在寻找最大概率的路径,而不是最小权重的路径,我们需要对传统的Dijkstra算法或Bellman-Ford算法进行一些修改。这里我将提供一个基于Dijkstra算法的变体,用于解决这个问题。
以下是Go语言实现的代码:
go
import (
"container/heap"
)
type Edge struct {
To int
Reli float64
}
type Graph struct {
Nodes [][]Edge
}
type Item struct {
Node int
Prob float64
index int
}
type PriorityQueue []*Item
func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool {
return pq[i].Prob > pq[j].Prob
}
func (pq PriorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].index = i
pq[j].index = j
}
func (pq *PriorityQueue) Push(x interface{}) {
n := len(*pq)
item := x.(*Item)
item.index = n
*pq = append(*pq, item)
}
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
old[n-1] = nil
item.index = -1
*pq = old[0 : n-1]
return item
}
func NewGraph(edges [][]Edge) *Graph {
g := &Graph{make([][]Edge, len(edges))}
for i, edgeList := range edges {
g.Nodes[i] = make([]Edge, len(edgeList))
copy(g.Nodes[i], edgeList)
}
return g
}
func MostReliablePath(g *Graph, start, end int) float64 {
pq := make(PriorityQueue, 0)
heap.Init(&pq)
dist := make([]float64, len(g.Nodes))
for i := range dist {
dist[i] = 0
}
dist[start] = 1 // 开始节点到自身的可靠性为1
heap.Push(&pq, &Item{Node: start, Prob: 1})
for pq.Len() > 0 {
item := heap.Pop(&pq).(*Item)
node := item.Node
if node == end {
break
}
for _, edge := range g.Nodes[node] {
newProb := dist[node] * edge.Reli
if newProb > dist[edge.To] {
dist[edge.To] = newProb
heap.Push(&pq, &Item{Node: edge.To, Prob: newProb})
}
}
}
return dist[end]
}
在这个实现中,我们定义了一个Edge
结构体来存储边的信息,包括目标节点和可靠性。Graph
结构体表示整个图,它包含一个切片,每个元素都是一个Edge
切片的列表,表示每个节点的出边。
我们还定义了一个优先队列(PriorityQueue
)和一个Item
结构体,用于在Dijkstra算法中跟踪每个节点的最大可靠性。
MostReliablePath
函数实现了我们的算法。它初始化距离数组和优先队列,然后开始遍历图,更新每个节点的最大可靠性,直到找到终点或遍历完所有可达节点。
要使用这个函数,你需要创建一个Graph
实例,并调用MostReliablePath
函数,传入起始节点和结束节点。函数将返回这两个节点之间的最可靠通信链路的可靠性。
智谱清言:
这个问题可以通过动态规划算法来解决,类似于求解最短路径的Floyd-Warshall算法。我们可以定义一个二维数组dp
,其中dp[i][j]
表示从结点i
到结点j
之间最可靠的通信链路的可靠性值。算法的基本思想是,通过考虑所有可能的中间结点,逐步更新dp
数组中的值。
以下是使用Go语言实现的算法:
go
package main
import (
"fmt"
"math"
)
// 定义图的结构
type Graph struct {
Vertices int
Edges map[int]map[int]float64
}
// 初始化图
func NewGraph(vertices int) *Graph {
return &Graph{
Vertices: vertices,
Edges: make(map[int]map[int]float64),
}
}
// 添加边
func (g *Graph) AddEdge(u, v int, r float64) {
if g.Edges[u] == nil {
g.Edges[u] = make(map[int]float64)
}
g.Edges[u][v] = r
}
// 计算最可靠的通信链路
func (g *Graph) MostReliablePath() [][]float64 {
// 初始化dp数组,所有值设为0
dp := make([][]float64, g.Vertices)
for i := range dp {
dp[i] = make([]float64, g.Vertices)
for j := range dp[i] {
if i == j {
dp[i][j] = 1 // 自己到自己的可靠性为1
} else {
dp[i][j] = 0
}
}
}
// 更新直接连接的边的可靠性
for u := 0; u < g.Vertices; u++ {
if g.Edges[u] != nil {
for v, r := range g.Edges[u] {
dp[u][v] = r
}
}
}
// 使用Floyd-Warshall算法更新dp数组
for k := 0; k < g.Vertices; k++ {
for i := 0; i < g.Vertices; i++ {
for j := 0; j < g.Vertices; j++ {
dp[i][j] = math.Max(dp[i][j], dp[i][k]*dp[k][j])
}
}
}
return dp
}
func main() {
g := NewGraph(4)
g.AddEdge(0, 1, 0.9)
g.AddEdge(0, 2, 0.5)
g.AddEdge(1, 2, 0.8)
g.AddEdge(2, 0, 0.7)
g.AddEdge(2, 3, 0.6)
g.AddEdge(3, 3, 1.0) // 自环
dp := g.MostReliablePath()
for i := range dp {
for j := range dp[i] {
fmt.Printf("从结点%d到结点%d的最可靠通信链路可靠性为: %.2f\n", i, j, dp[i][j])
}
}
}
在这个代码中,我们首先定义了一个图的结构,并提供了添加边的方法。然后,我们实现了MostReliablePath
方法来计算任意两个结点之间最可靠的通信链路。最后,在main
函数中,我们创建了一个图,添加了一些边,并计算了最可靠的通信链路。
这个算法的时间复杂度是 O ( V 3 ) O(V^3) O(V3),其中 V V V是图中结点的数量。对于稠密图来说,这是一个有效的算法。如果图非常稀疏,可以考虑使用其他算法,如基于优先队列的Dijkstra算法变种,来提高效率。
天工:
为了找到有向图中任意两点间最可靠的通信路径,我们可以使用类似于Dijkstra算法的方法,但需要进行一些调整以适应可靠性这一特性。由于我们关心的是最大概率路径,我们需要在每一步中都选择能够最大化路径总可靠性的边。在概率乘法原理下,路径的总可靠性是路径上每条边的可靠性相乘的结果。
算法步骤
-
初始化:为起点s创建一个空的优先队列Q,并初始化距离数组d,其中d[s] = 1(起点到自身的可靠性为1),其余节点的可靠性为0。同时,创建一个前驱节点数组p,用于记录最短路径上的前驱节点。
-
循环处理 :从优先队列Q中取出可靠性最大的节点u(初始化时为起点s),然后遍历u的所有出边(u, v)。对于每个邻接点v,计算通过u到达v的可靠性:
newReliability = d[u] * r(u, v)
。如果newReliability > d[v]
,则更新d[v]和p[v]。 -
更新优先队列:更新后的节点v需要被重新加入优先队列中,以便在下一轮中被正确处理。
-
重复步骤2和3,直到优先队列Q为空。
Go代码实现
go
package main
import (
"container/heap"
"fmt"
)
type Node struct {
id int
reliabl float64
index int
heapIdx int
}
type PriorityQueue []*Node
func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool {
return pq[i].reliabl > pq[j].reliabl
}
func (pq PriorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].heapIdx = i
pq[j].heapIdx = j
}
func (pq *PriorityQueue) Push(x interface{}) {
n := len(*pq)
node := x.(*Node)
node.heapIdx = n
*pq = append(*pq, node)
}
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
node := old[n-1]
old[n-1] = nil
node.heapIdx = -1
*pq = old[0 : n-1]
return node
}
func updatePriorityQueue(pq *PriorityQueue, node *Node) {
heap.Fix(pq, node.heapIdx)
}
func maxReliabilityPath(graph [][]float64, start int) {
n := len(graph)
d := make([]float64, n)
p := make([]int, n)
for i := range d {
d[i] = 0
p[i] = -1
}
d[start] = 1
pq := &PriorityQueue{}
heap.Init(pq)
node := &Node{start, 1, start, -1}
heap.Push(pq, node)
for pq.Len() > 0 {
u := heap.Pop(pq).(*Node)
for v := range graph[u.id] {
newReliability := d[u.id] * graph[u.id][v]
if newReliability > d[v] {
d[v] = newReliability
p[v] = u.id
node := &Node{v, newReliability, v, -1}
heap.Push(pq, node)
updatePriorityQueue(pq, node)
}
}
}
fmt.Println("Reliability distances:", d)
fmt.Println("Predecessor array:", p)
}
func main() {
graph := [][]float64{
{0, 0.5, 0.2, 0},
{0, 0, 0.1, 0.7},
{0, 0, 0, 0.3},
{0, 0, 0, 0},
}
maxReliabilityPath(graph, 0) // Assuming 0 is the start node
}
说明
- 上述代码中,
graph
是一个邻接矩阵,其中graph[u][v]
表示边(u, v)的可靠性。 maxReliabilityPath
函数计算从指定起点到所有其他节点的最可靠路径。- 使用了优先队列来保证每次处理的节点是最可靠的。
d
数组存储了最可靠路径的可靠性,而p
数组存储了最短路径上的前驱节点。
chatgpt:
要找到任意两个结点之间最可靠的通信链路,可以使用动态规划算法来解决。以下是一个用Go语言实现的示例代码:
go
package main
import (
"fmt"
"math"
)
func maxReliablePath(G [][]float64, start, end int) float64 {
n := len(G)
dp := make([][]float64, n)
for i := range dp {
dp[i] = make([]float64, n)
}
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
dp[i][j] = G[i][j]
}
}
for k := 0; k < n; k++ {
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
dp[i][j] = math.Max(dp[i][j], dp[i][k]*dp[k][j])
}
}
}
return dp[start][end]
}
func main() {
G := [][]float64{
{0, 0.5, 0.8},
{0.4, 0, 0.1},
{0.2, 0.9, 0},
}
start := 0
end := 2
result := maxReliablePath(G, start, end)
fmt.Printf("The maximum reliability between node %d and node %d is: %.2f\n", start, end, result)
}
在示例代码中,我们使用一个二维数组G
来表示图中每条边的可靠性值。函数maxReliablePath
接受一个邻接矩阵和起点、终点的索引,然后计算从起点到终点的最大可靠性。
你可以根据自己的实际需求修改G
和起点、终点的值,然后运行这段代码来得到任意两个结点之间最可靠的通信链路的可靠性值。
kimi:
为了找到有向图 ( G = (V, E) ) 中任意两个结点之间最可靠的通信链路,我们可以使用 Dijkstra 算法的变体,该算法考虑了边的可靠性。以下是算法的逐步解释和 Go 语言的实现:
算法步骤:
- 初始化:对于每个顶点 ( v \in V ),设置一个距离数组 ( dist[v] ) 来存储从源顶点 ( s ) 到顶点 ( v ) 的最可靠路径的概率。将 ( s ) 的距离设置为 1(因为从自身到自身的可靠性是 1),将所有其他顶点的距离设置为 0。
- 优先队列:使用一个优先队列(最小堆)来存储顶点及其距离,以便每次都能扩展最可靠的顶点。
- 松弛操作:对于每个顶点 ( u ),检查其所有出边 ( (u, v) )。如果通过 ( u ) 到 ( v ) 的路径比当前已知的路径更可靠,则更新 ( v ) 的距离并将 ( v ) 重新加入优先队列。
- 终止条件:当优先队列为空或找到目标顶点 ( t ) 时,算法终止。
Go 语言实现:
go
package main
import (
"container/heap"
"fmt"
)
type Edge struct {
To int
Reliability float64
}
type Vertex struct {
Name int
Edges []*Edge
}
type PriorityQueue []*Vertex
func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool {
return pq[i].dist > pq[j].dist
}
func (pq PriorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
}
func (pq *PriorityQueue) Push(x interface{}) {
*pq = append(*pq, x.(*Vertex))
}
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
x := old[n-1]
*pq = old[0 : n-1]
return x
}
func (v *Vertex) updateDistance(newDist float64) {
if newDist > v.dist {
v.dist = newDist
heap.Push(&pq, v)
}
}
func findMostReliablePath(graph []*Vertex, src, dst int) float64 {
pq := make(PriorityQueue, 0)
heap.Init(&pq)
for _, v := range graph {
v.dist = 0
}
graph[src].dist = 1
heap.Push(&pq, graph[src])
for pq.Len() > 0 {
u := heap.Pop(&pq).(*Vertex)
if u.Name == dst {
return u.dist
}
for _, edge := range u.Edges {
v := edge.To
newDist := u.dist * edge.Reliability
graph[v].updateDistance(newDist)
}
}
return 0
}
func main() {
// Example graph construction
vertices := []*Vertex{
{Name: 0}, {Name: 1}, {Name: 2}, {Name: 3},
}
edges := []*Edge{
{To: 1, Reliability: 0.9}, {To: 2, Reliability: 0.7},
{To: 2, Reliability: 0.8}, {To: 3, Reliability: 0.5},
{To: 3, Reliability: 0.6}, {To: 1, Reliability: 0.4},
}
for i, edge := range edges {
vertices[edge.To].Edges = append(vertices[edge.To].Edges, &Edge{To: i, Reliability: edge.Reliability})
}
src, dst := 0, 3
fmt.Printf("Most reliable path from %d to %d has reliability: %f\n", src, dst, findMostReliablePath(vertices, src, dst))
}
说明:
- 这段代码定义了一个有向图,其中包含顶点和边。
- 它使用优先队列来找到从源顶点到目标顶点的最可靠路径。
findMostReliablePath
函数返回两个顶点之间最可靠路径的可靠性。
这个算法确保了在每一步都扩展最可靠的顶点,从而在终止时找到最可靠的路径。
混元:
要找到有向图中任意两个节点之间最可靠的通信路径,我们可以使用Dijkstra算法的变种,因为我们需要找到从起点到终点的最大概率(即最可靠路径)。以下是解决这个问题的思路:
-
初始化:创建一个图表示所有节点和边的可靠性。对于每个节点,初始化一个到自身的可靠性为1(因为从任何节点到自身的路径总是可靠的),其他节点的可靠性初始化为0。
-
使用优先队列(最大堆)来存储节点和它们的可靠性,初始时将起始节点放入队列中。
-
当优先队列不为空时,重复以下步骤:
a. 从队列中取出可靠性最高的节点。
b. 对于该节点的每个邻居,计算通过当前节点到达邻居的可靠性(即当前节点的可靠性乘以边的可靠性)。
c. 如果这个新的可靠性大于邻居当前的可靠性,则更新邻居的可靠性,并将邻居及其新的可靠性放入优先队列中。
-
当终点节点被处理后,其可靠性就是从起点到终点的最可靠路径的可靠性。
-
为了找到具体的路径,我们可以在更新邻居的可靠性时,记录每个节点的前驱节点。这样,在找到最可靠路径的可靠性后,我们可以从终点回溯到起点,构建出完整的路径。
以下是Go语言实现的代码:
go
package main
import (
"container/heap"
"fmt"
)
type Edge struct {
to int
reliability float64
}
type Node struct {
index int
reliability float64
}
type PriorityQueue []*Node
func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool {
return pq[i].reliability > pq[j].reliability
}
func (pq PriorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].index = i
pq[j].index = j
}
func (pq *PriorityQueue) Push(x interface{}) {
n := len(*pq)
node := x.(*Node)
node.index = n
*pq = append(*pq, node)
}
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
node := old[n-1]
node.index = -1 // for safety
*pq = old[0 : n-1]
return node
}
func findReliablePath(graph [][]Edge, start, end int) (float64, []int) {
n := len(graph)
reliabilities := make([]float64, n)
previous := make([]int, n)
for i := range reliabilities {
reliabilities[i] = 0
previous[i] = -1
}
reliabilities[start] = 1
pq := make(PriorityQueue, 0)
heap.Push(&pq, &Node{index: start, reliability: 1})
for pq.Len() > 0 {
current := heap.Pop(&pq).(*Node)
if current.index == end {
break
}
for _, edge := range graph[current.index] {
newReliability := current.reliability * edge.reliability
if newReliability > reliabilities[edge.to] {
reliabilities[edge.to] = newReliability
previous[edge.to] = current.index
heap.Push(&pq, &Node{index: edge.to, reliability: newReliability})
}
}
}
// Reconstruct path
path := make([]int, 0)
for current := end; current != -1; current = previous[current] {
path = append([]int{current}, path...)
}
return reliabilities[end], path
}
func main() {
graph := [][]Edge{
{{1, 0.9}, {2, 0.8}},
{{2, 0.7}, {3, 0.6}},
{{3, 0.5}},
{},
}
start := 0
end := 3
reliability, path := findReliablePath(graph, start, end)
fmt.Printf("The most reliable path from %d to %d has reliability %.2f and the path is %v\n", start, end, reliability, path)
}
在这段代码中,我们定义了Edge
结构体来表示图中的边和它们的可靠性,Node
结构体来表示优先队列中的节点和它们的可靠性,以及PriorityQueue
类型来实现一个最大堆。findReliablePath
函数实现了寻找最可靠路径的算法,并返回路径的可靠性和路径本身。最后,main
函数提供了一个示例图,并调用findReliablePath
函数来找到从节点0到节点3的最可靠路径。