Go语言数据结构和算法(三十八)动态规划Floyd-Warshall算法

16 阅读4分钟

动态规划是计算机编程中一种技术.有效的解决具有重叠子问题和最优子结构属性的

一类问题.动态规划简化复杂问题的方法是将复杂问题以递归的方式分解成更简单的

子问题.跨域多个时间点的决策通常是递归分解的.尽管有些决策问题不能分解.

1.特点:

重叠子问题:

子问题是原始问题的较小版本.如果找到解决方案涉及多次解决相同的子问题.则这些

问题都有重叠子问题.

最佳子结构属性:

如果任何问题的整体最优解可以从其子问题的最优解中构造出来.则任何问题都具有

最优子结构性质.

2.动态规划常见算法:

2.1:Floyd-Warshall算法:

该算法用于在加权图中找到所有顶点之间的最短路径.

2.2:最长公共子序列:

该算法用于查找两个或多个序列共有的最长子序列.

2.3背包问题:

该算法用于确定通过选择一组受重量约束的项目可以获得的最大值.

2.4矩阵链乘法:

该算法用于找到最有效的方法乘以一组矩阵.

2.5硬币找零问题:

该算法用于确定找零给定金额所需的最少硬币数量.

3.使用场景:

3.1优化问题:

动态规划通常用于解决优化问题.开发者试图从所有可能的解决方案中找到最佳解决

方案.

3.2寻路算法:

动态规划也可用于寻路算法.开发者可以在其中尝试找到两点之间的最短或成本最低

的路径.

3.3序列对比:

在生物信息学中.动态规划通常用于比对DNA RNA 或蛋白质序列.这是许多生物分析

中重要的步骤.

3.4博弈论:

动态规划可用于博弈论,以在多轮或多阶段的博弈中为玩家找到最优策略.

4.Floy-Warshall算法:

是一种用于在加权图中找到所有顶点对之间的最短路径的算法.该算法适用于有向加

权图和无向加权图.

5.Floy-Warshall算法特征:

5.1:初始化并输入与图矩阵相同的解矩阵,然后通过将所有顶点视为中间顶点来更新矩阵.

这个想法是一个一个地挑选所有顶点并更新所有最短路径.其中包括被挑选的顶点作

为最短路径中的中间顶点.

5.2:当选择顶点号k作为中间顶点时.会将顶点{0,1,2,...,k-1}视为中间顶点.对于源顶

点和目标顶点的每一对(i,j).

如果k不是从i到j的最短路径中的中间顶点.则保持dist[i][j]的值不变.

如果k是从i到j的最短路径中的一个中间顶点.且dist[i][j]>dist[i][k]+dist[k][j].则

将dist[i][j]的值更新为dist[i][k]+dist[k][j].

6.Floy-Warshall算法场景:

6.1网络路由和物流:

可用于查找图中每对顶点之间的最短路径.这在网络路由和物流等应用很有用.

6.2求图的直径:

可用于求图的直径.即图中任意两个顶点之间最长/最短路径的长度.

6.3优化交通流量:

可用于通过找到网络中所有顶点对之间的最短路径并相应的调整交通流量来优化网络

中的交通流量.

7.实现:

7.1方法:

package data

import "math"

// 加权图对象.
type FloydGraph struct {
    To     int
    Weight float64
}

func FloydWarshall(g [][]FloydGraph) [][]float64 {
    distance := make([][]float64, len(g))
    for i := range distance {
       dist := make([]float64, len(g))
       for j := range dist {
          dist[j] = math.Inf(1)
       }
       dist[i] = 0
       distance[i] = dist
    }
    for u, Graphs := range g {
       for _, v := range Graphs {
          distance[u][v.To] = v.Weight
       }
    }
    for k, dk := range distance {
       for _, di := range distance {
          for j, dij := range di {
             if d := di[k] + dk[j]; dij > d {
                di[j] = d
             }
          }
       }
    }
    return distance
}

7.2main方法:

package main

import (
    "fmt"
    "gomodule/data"
    _ "gomodule/pubsub"
)

func main() {
    graph := [][]data.FloydGraph{
       1: {{5, 9}, {2, 3}, {3, -5}},
       2: {{2, 5}, {5, 6}},
       3: {{3, 6}, {4, 8}},
       4: {{3, 7}, {1, -3}},
       5: {{1, 0}},
    }
    warshall := data.FloydWarshall(graph)

    for _, d := range warshall {
       fmt.Printf("%4g\n", d)
    }
}

8.实战:

8.1方法:

func FindCity(n int, edges [][]int, distanceLong int) int {
    distance := make([][]int, n)
    for i := range distance {
       distance[i] = make([]int, n)
       for j := range distance[i] {
          distance[i][j] = 10000000
       }
    }
    for i := range distance {
       distance[i][i] = 0
    }
    for i := range edges {
       distance[edges[i][0]][edges[i][1]] = edges[i][2]
       distance[edges[i][1]][edges[i][0]] = edges[i][2]
    }

    for k := range distance {
       for i := range distance {
          for j := range distance {
             distance[i][j] = minFloyd(distance[i][k]+distance[k][j], distance[i][j])
          }
       }
    }
    res := make([]int, n)
    for i := range distance {
       for j := range distance[i] {
          if i != j && distance[i][j] <= distanceLong {
             res[i]++
          }
       }
    }
    result := 0
    cur := res[0]
    for i := range res {
       if res[i] <= cur {
          result = i
          cur = res[i]
       }
    }
    return result
}

func minFloyd(x, y int) int {
    if x < y {
       return x
    }
    return y
}

8.2main方法:

package main

import (
	"fmt"
	"gomodule/data"
	_ "gomodule/pubsub"
)

func main() {
	edges := [][]int{{0, 1, 2}, {1, 2, 3}, {2, 3, 4}, {1, 3, 1}}
	n := 4
	distanceLong := 4
	city := data.FindCity(n, edges, distanceLong)
	fmt.Println(city)
}

无言独上西楼.寂寞梧桐深院锁清秋.

如果大家喜欢我的分享的话.可以关注我的微信公众号

念何架构之路